Tổng hợp về phương thức tìm kiếm FIND (Find Method)

Liên hệ QC

hoangdanh282vn

Nguyễn Cảnh Hoàng Danh
Thành viên danh dự
Tham gia
21/12/07
Bài viết
1,900
Được thích
5,276
Nghề nghiệp
Kinh doanh các mặt hàng văn phòng phẩm
Find Method

Phương thức Find tìm kiếm thông tin trong một vùng nào đó, kết quả trả về là ô đầu tiên chứa đựng thông tin được tìm thấy. Nếu không tìm thấy thông tin trong vùng tìm kiếm thì phương thức Find sẽ trả về Nothing.

Cú pháp phương thức như sau :

Expression.Find(What, After, LookIn, LookAt, SearchOrder, SearchDirection, MatchCase, MatchByte, SearchFormat)

Expression : Vùng tìm kiếm. Biểu thức Expression phải được khai báo và nó trả về một đối tượng Range.

What : Dữ liệu hay thông tin cần tìm kiếm. Đây là một đòi hỏi bắt buộc phải khai báo. Dữ liệu tìm kiếm có thể là một chuỗi ký tự hay một dạng dữ liệu nào đó có trong Excel và được khai báo dưới dạng Variant.

After : Ô được chọn để xác định vị trí tìm kiếm, là tùy chọn, được khai báo dưới dạng Variant, có thể bỏ qua. Việc tìm kiếm sẽ bắt đầu từ ô này. Ô này tương ứng với vị trí của ô hiện hành sau khi việc tìm kiếm hoàn tất. Ô xác định vị trí tìm kiếm này sẽ không được đưa vào quá trình tìm kiếm trừ khi vùng tìm kiếm bao gồm cả ô này. Nếu đối số này không được khai báo thì việc tìm kiếm sẽ bắt đầu sau ô trên cùng bên trái của vùng cần tìm kiếm.

LookIn : Không bắt buộc, khai báo dạng Variant. Đây là một dạng thông tin và thường có giá trị là xlValues.

LookAt : Cách thức tìm kiếm. Là đối số tùy chọn, không bắt buộc, khai báo dưới dạng Variant. LookAt có 2 giá trị : xlWhole (Tìm toàn bộ) hoặc xlPart (tìm một phần).

SearchOrder : Xác định dạng thứ tự tìm kiếm, là tùy chọn không bắt buộc, khai báo dưới dạng Variant. SearchOrder có 2 dạng ứng với 2 hàng số : xlByRows (theo thứ tự dòng) hoặc xlByColumns (theo thứ tự cột)

SearchDirection : Hướng tìm kiếm, là tùy chọn. SearchDirection có 2 dạng ứng với 2 hằng số : xlNext (Tìm kế tiếp, là giá trị mặc định), xlPrevious (Tìm trước đó)

MatchCase : Là tùy chọn để xác định kiểu tìm kiếm có phân biệt chữ Hoa với chữ thường, khai báo dưới dạng Variant. Khai báo là True nếu ta muốn tìm kiếm chính xác. Giá trị mặc định là False.( Không phân biệt chữ in Hoa với chữ thường)

MatchByte : Không bắt buộc, khai báo dạng Variant. Chỉ sử dụng khi ta đã chọn hoặc cài đặt bộ hỗ trợ ngôn ngữ ký tự byte kép. Là True nếu ứng với bộ ký tự byte kép, False nếu ứng với bộ ký tự byte đơn.

SearchFormat : Tìm kiếm theo định dạng. Là tham số tùy chọn, khai báo dưới dạng Variant.

Lưu ý :

- Các thiết lập cho các đối số LookIn, LookAt, SearchOrder MatchByte sẽ được lưu mỗi lần ta sử dụng phương thức này (phương thức Find). Nếu ta không khai báo giá trị cho các đối số vào lần sử dụng phương thức Find tiếp theo, các giá trị thiết lập đã lưu trước đó sẽ được sử dụng. Việc thiết lập các đối số này làm thay đổi các tùy chọn thiết lập trong hộp thoại Find, và việc thay đổi các thiết lập trong hộp thoại Find sẽ làm thay đổi các giá trị đã lưu – là những giá trị được sử dụng nếu ta bỏ qua các đối số này. Để tránh xảy ra việc này, ta nên khai báo các đối số một cách rõ ràng mỗi lần sử dụng phương thức Find này.

- Ta có thể dùng phương thức FindNextFindPrevious để lặp lại việc tìm kiếm. Khi đến vị trí cuối của vùng tìm kiếm được xác định trước đó, excel sẽ bao phủ từ vị trí này đến vị trí đầu tiên của vùng tìm kiếm. Để ngưng việc tìm kiếm ngay khi động tác bao phủ này xảy ra, hãy lưu lại địa chỉ của ô đầu tiên tìm được, sau đó thử so sánh lần lượt mỗi địa chỉ ô được tìm thấy kế tiếp với địa chỉ ô vừa được lưu này.

============================@@@===========================​

Bài viết này tham khảo dựa trên bài dịch nguyên bản của anh Ca_dafi
(Còn tiếp)
 

File đính kèm

  • General about Find Method.rar
    53.4 KB · Đọc: 2,644


''Tai sao khong the thuc hien dung khi co them mot FIND METHOD nua???

'rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 1).ClearContents
End Sub

Không thể được bạn thân mến à!
Mà chú mày chạy thử code này xem sao:

PHP:
Option Explicit
Sub TEST()
    Dim rngNhap As Range, rngXuat As Range
    Dim curRng As String, fstRng As String
    Dim J As Long
    
    ''restore:
    Sheet2.[D3].Resize(20, 3).Copy Destination:=Sheet2.[A3]
    
    MsgBox "Restore OK!"
    
    Set rngNhap = Sheet1.Range("A3:A18")
    
    With Sheet2.Range("B3:B12")
        
        Set rngXuat = .Find(What:="KH_1", LookIn:=xlFormulas, LookAt:=xlWhole)
        If Not rngXuat Is Nothing Then
            fstRng = rngXuat.Address
            Do
                MsgBox "Dia chi duoc tim thay: " & rngXuat.Address
                J = J + 1
                ''Tai sao khong the thuc hien dung khi co them mot FIND METHOD nua???
                'rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 1).ClearContents
                
                rngXuat.Offset(, -1).Resize(, 2).Interior.ColorIndex = 34 + J
                rngXuat.Offset(, 1).Value = J
                
                Set rngXuat = .FindNext(rngXuat)
                If rngXuat Is Nothing Then Exit Do
            Loop Until rngXuat.Address = fstRng
        End If
    End With
End Sub

Có thể thử như sau:
Đang xài FIND() & trong đó có FINNEXT() rồi thì không xài FIND() được nựa nữa ngay cả trong macro đó,
Có thể xài FIND() nhiều lần & cuối macro là FINDNEXT() thì được.

Mình rút ra từ thực nghiệm thôi!

Cũng giống như tìm trong ô trộn ngang hay ô trộn dọc của bài trước đây vậy!

Thân!
 
Upvote 0
Nếu tôi thực hiện 1 kiểu FindNext trong 1 vùng dữ liệu thì làm đúng, nhưng sau khi tôi tìm dữ liệu có từ FindNext để làm cơ sở tìm kiếm 1 vùng dữ liệu khác thì nó chỉ thực hiện đúng 1 trường hợp đầu tiên. Tại sao?

Mã:
Option Explicit


Sub TEST()
    Dim rngNhap As Range, rngXuat As Range
    Dim curRng As String, fstRng As String
    
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    
    MsgBox "Restore OK!"
    
    Set rngNhap = Sheet1.Range("A3:A18")
    
    With Sheet2.Range("B3:B12")
        
        Set rngXuat = .Find(What:="KH_1", LookIn:=xlFormulas, LookAt:=xlWhole)
        If Not rngXuat Is Nothing Then
            fstRng = rngXuat.Address
            Do
                MsgBox "Dia chi duoc tim thay: " & rngXuat.Address
                
[COLOR=#0000ff]                ''Tai sao khong the thuc hien dung khi co them mot FIND METHOD nua???[/COLOR]
[B][COLOR=#ff0000]                'rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 1).ClearContents[/COLOR][/B]
                
                rngXuat.Offset(, -1).Resize(, 2).ClearContents
                Set rngXuat = .FindNext(rngXuat)
                If rngXuat Is Nothing Then Exit Do
            Loop Until rngXuat.Address = fstRng
        End If
    End With
End Sub

Sao nhiều người lập trình mà không đọc help nhỉ? Thường thì người lập trình đọc help - có trong phần
mềm, tìm được trên mạng, trong sách - đến nát bươm mà vẫn không thôi.

Tại sao? Tại sao? Tại bạn không đọc help. Hoặc đọc mà không hiểu.

Sếp ra lệnh: tìm một thằng có biệt hiệu là "béo". Bạn nói: đạ thưa sếp, em tìm thấy rồi. Sếp nói: "tìm
tiếp". Bạn tìm một thằng "béo" thứ hai? Sếp có nói tìm ai đâu, sếp chỉ nói: tìm tiếp. Thế mà bạn hiểu.
Sau đó sếp nói: tìm một thằng có biệt hiệu là "lác". Sau đó sếp nói: "tìm tiếp". Không nói tìm ai nhưng
bạn hiểu là tìm tiếp thằng "lác", đúng không? À, mà tại sao bạn biết tìm tiếp thằng "lác" chứ không phải
thằng "béo"?

Find/FindNext cũng có lôgíc tương tự. Tại sao bạn phải nhập What vào Find? Vì nếu không có thì "tìm
gió trong đồng trống" à? Hơi bị lôgíc, đúng không? Thế tại sao lại không nhập "tìm gì" vào FindNext?
Nếu nhập đầy đủ như Find thì còn sinh ra FindNext làm gì? Cứ một thằng Find là đủ, đúng không? Vậy
FindNext là "tìm tiếp" cái đã được thiết lập trong Find, mà là trong Find cuối cùng ("lác" chứ không
phải "béo"). Cũng hơi bị lôgíc, đúng không?

Nói có sách:

Mã:
Range.FindNext Method 
Continues a search that was begun with the Find method. Finds the next cell that [COLOR=#ff0000]matches those same 
[B]conditions[/B][/COLOR] and returns a Range object that represents that cell. Doesn’t affect the selection or the 
active cell

Bây giờ là code có cả dòng đỏ đỏ

Mã:
Sub TEST()
    Dim rngNhap As Range, rngXuat As Range
    Dim curRng As String, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value    
    MsgBox "Restore OK!"    
    Set rngNhap = Sheet1.Range("A3:A18")    
    With Sheet2.Range("B3:B12")        
        Set rngXuat = .Find(What:="KH_1", LookIn:=xlFormulas, LookAt:=xlWhole)
        If Not rngXuat Is Nothing Then
            fstRng = rngXuat.Address
            Do
                MsgBox "Dia chi duoc tim thay: " & rngXuat.Address                
                [COLOR=#ff0000]rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 
1).ClearContents[/COLOR]                
                rngXuat.Offset(, -1).Resize(, 2).ClearContents
                Set rngXuat = .FindNext(rngXuat)
                [COLOR=#0000ff]If rngXuat Is Nothing Then Exit Do[/COLOR]
            Loop Until rngXuat.Address = fstRng
        End If
    End With

Ở vòng lặp Do đầu tiên ở chỗ đỏ đỏ bạn thiết lập tìm "NGHIA_2" do What = rngXuat.Offset(, -1) = "NGHIA_2". Vậy FindNext tìm tiếp "NGHIA_2" trong vùng Sheet2.Range("B3:B12"). Rõ ràng trong vùng Sheet2.Range("B3:B12") đâu có "NGHIA_2". Vậy kết quả trả về là Nothing, tức rngXuat = Nothing, và do vậy ra khỏi vòng lặp Do (dòng xanh xanh)
 
Lần chỉnh sửa cuối:
Upvote 0
Sao nhiều người lập trình mà không đọc help nhỉ? Thường thì người lập trình đọc help - có trong phần
mềm, tìm được trên mạng, trong sách - đến nát bươm mà vẫn không thôi.

Tại sao? Tại sao? Tại bạn không đọc help. Hoặc đọc mà không hiểu.

Sếp ra lệnh: tìm một thằng có biệt hiệu là "béo". Bạn nói: đạ thưa sếp, em tìm thấy rồi. Sếp nói: "tìm
tiếp". Bạn tìm một thằng "béo" thứ hai? Sếp có nói tìm ai đâu, sếp chỉ nói: tìm tiếp. Thế mà bạn hiểu.
Sau đó sếp nói: tìm một thằng có biệt hiệu là "lác". Sau đó sếp nói: "tìm tiếp". Không nói tìm ai nhưng
bạn hiểu là tìm tiếp thằng "lác", đúng không? À, mà tại sao bạn biết tìm tiếp thằng "lác" chứ không phải
thằng "béo"?

Find/FindNext cũng có lôgíc tương tự. Tại sao bạn phải nhập What vào Find? Vì nếu không có thì "tìm
gió trong đồng trống" à? Hơi bị lôgíc, đúng không? Thế tại sao lại không nhập "tìm gì" vào FindNext?
Nếu nhập đầy đủ như Find thì còn sinh ra FindNext làm gì? Cứ một thằng Find là đủ, đúng không? Vậy
FindNext là "tìm tiếp" cái đã được thiết lập trong Find, mà là trong Find cuối cùng ("lác" chứ không
phải "béo"). Cũng hơi bị lôgíc, đúng không?

Hỏi là gây chú ý vấn đề Find mà chưa có người nào đề cập tới thôi, chứ theo em hiểu thì "sếp" bảo tìm những thằng "béo" và sếp cũng ra lệnh rằng, từ những thằng "béo" sẽ khai ra những thằng "lác" nó ở đâu.

Từ việc dùng 2 thằng Find này mới biết rằng chúng chỉ dùng chung một công cụ hoạt động, nếu có một câu lệnh khác trên công cụ này thì công cụ này sẽ chuyển hướng khác ngay.

Nó khác hoàn toàn nếu ta thực hiện trên Range hay trên Array, việc nào ra việc đó, vùng nào ra vùng đó, không lằng nhằng, chồng chéo gì cả!
 
Upvote 0
Hỏi là gây chú ý vấn đề Find mà chưa có người nào đề cập tới thôi, chứ theo em hiểu thì "sếp" bảo tìm những thằng "béo" và sếp cũng ra lệnh rằng, từ những thằng "béo" sẽ khai ra những thằng "lác" nó ở đâu.

Từ việc dùng 2 thằng Find này mới biết rằng chúng chỉ dùng chung một công cụ hoạt động, nếu có một câu lệnh khác trên công cụ này thì công cụ này sẽ chuyển hướng khác ngay.

Nó khác hoàn toàn nếu ta thực hiện trên Range hay trên Array, việc nào ra việc đó, vùng nào ra vùng đó, không lằng nhằng, chồng chéo gì cả!

Gọi phương thức Find của range nào thì nôm na là vùng tìm sẽ là range đó. Thế thôi. Còn việc tìm gì do FindNext thực hiện thì được xác định ở Find. Lôgíc nó là thế và help cũng viết rõ ràng. Còn nếu bạn thắc mắc là lấy ở Find cuối hay lấy ở Find nào thì cũng giải thích được thỏa đáng. Giả sử có thể lấy ở Find thứ xyz thì rõ ràng phải nhập cái xyz đó vào. Vì nếu không thì biết lấy ở Find 1, 2, hay 100? Vậy thì nếu không phải nhập, không có chỗ nào nhập thì chỉ còn nước là hiểu lấy ở Find "mới nhất".
Tôi đã cho bạn vd. thực tiễn. Vì những người viết code cho Find/FindNext người ta cũng có cái lôgíc như trong "đời thường". Sếp nói: Tìm thằng "béo". Sau đó sếp nói: "tìm tiếp" (cụt lủn) thì bạn hiểu là tìm tiếp thằng "béo" sau thằng vừa tìm được. Một lúc sau sếp nói: Tìm thằng "lác". Sau đó sếp nói: "tìm tiếp" (cũng cụt lủn) nhưng bạn hiểu ngay là tìm tiếp thằng "lác" chứ không phải tìm tiếp thằng "béo", đúng không?
Nếu bạn lập trình trong Windows API thì bạn thấy nhiều trường hợp có lôgíc như thế. Vd. hàm FindFirstFile/FindNextFile. Tìm gì thì bạn phải nhập vào FindFirstFile. Nếu bạn muốn tìm tiếp cái cần tìm thì bạn gọi FindNextFile nhưng không phải nhập cái cần tìm nữa. Vì nó chẳng qua là "Tìm tiếp". Mà FindNext chính là "tìm tiếp" chứ là cái gì? Bạn có thể tìm tới khi không tìm thấy nữa mới thôi bằng cách cứ gọi FindNextFile trong vòng lặp. Tất nhiên ở thời điểm bất kỳ bạn có thể kết thúc tìm.

Bạn thấy, bạn cho là "lằng nhằng"?. Nếu bạn đọc kỹ help và hiểu từng câu chứ không phải là đọc lướt qua thì bạn thấy nó lôgíc. Mà cho dù bạn cho là không lôgíc thì thôi bạn cứ nhớ là Find/FindNext nó có cái lôgíc quái gở như thế. Và viết code đúng cái lôgíc quái đản như thế là được. Thế thôi.

Mà code người ta viết như thế bạn thấy dùng được thì dùng. Và muốn dùng thì phải theo cái "code" đó. Người ta viết rồi người ta giải thích trong help là code của người ta nó hoạt động thế nào mà bạn làm sai đi rồi bạn trách người ta "lằng nhằng"? Khó hiểu thật.
 
Upvote 0
Mà code người ta viết như thế bạn thấy dùng được thì dùng. Và muốn dùng thì phải theo cái "code" đó. Người ta viết rồi người ta giải thích trong help là code của người ta nó hoạt động thế nào mà bạn làm sai đi rồi bạn trách người ta "lằng nhằng"? Khó hiểu thật.

Để nó hoạt động thì chúng ta phải đặt Find vào trong vòng lặp, và "kêu" đích danh thằng "lính", sau đó, phải kêu tiếp thằng "lính" khác sau khi đã tìm được thằng "lính" đầu, mà không phải FindNext:

Mã:
Sub TEST()
    Dim rngXuat As Range, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    MsgBox "Restore OK!"
    
    Set rngXuat = Sheet2.Range("B3")
    fstRng = rngXuat.Address
    Do
        Set rngXuat = Sheet2.Range("B3:B12").Find([B][COLOR=#0000ff]What:="KH_1", _[/COLOR][/B]
[COLOR=#ff0000][B]                                                  After:=rngXuat, _[/B][/COLOR]
                                                  LookIn:=xlFormulas, _
                                                  LookAt:=xlWhole)
        If rngXuat Is Nothing Then Exit Do
        
        MsgBox "Dia chi duoc tim thay: " & rngXuat.Address
        
        Sheet1.Range("A3:A18").Find(What:=rngXuat.Offset(, -1), _
                                    LookIn:=xlFormulas, _
                                    LookAt:=xlWhole).Offset(, 1).ClearContents
                                    
        rngXuat.Offset(, -1).Resize(, 2).ClearContents
    Loop Until rngXuat.Address = fstRng
End Sub

Có như thế mới không sợ lộn thằng "lính" với thằng "lác"!
 
Lần chỉnh sửa cuối:
Upvote 0
Để nó hoạt động thì chúng ta phải đặt Find vào trong vòng lặp, và "kêu" đích danh thằng "lính", sau đó, phải kêu tiếp thằng "lính" khác sau khi đã tìm được thằng "lính" đầu, mà không phải FindNext:

Chẳng phải bài 61 HYen 17 đã nói:

Có thể thử như sau:
Đang xài FIND() & trong đó có FINNEXT() rồi thì không xài FIND() được nựa nữa ngay cả trong macro đó,
Có thể xài FIND() nhiều lần & cuối macro là FINDNEXT() thì được.
 
Upvote 0
Chẳng phải bài 61 HYen 17 đã nói:

Có thể thử như sau:
Đang xài FIND() & trong đó có FINNEXT() rồi thì không xài FIND() được nựa nữa ngay cả trong macro đó,
Có thể xài FIND() nhiều lần & cuối macro là FINDNEXT() thì được.

Ở đây có một tham số mà ta không cần phải FindNext đó chính là After:=NextRange. Với Find Method thì cách hoạt động của nó rất giống với Range ở điểm, nếu ta không xác định tên sheet, thì xem như địa chỉ là sheet hiện hành; vì thế với Find ta cũng phải xác định cho nó vùng nào là vùng tham chiếu, điều kiện nào là điều kiện dò tìm thì nó mới đảm bảo thực hiện được yêu cầu của mình, nếu không, nó lấy kết quả của lần tìm kiếm cuối cùng làm điều kiện What cho lần tiếp theo.
 
Upvote 0
Ở đây có một tham số mà ta không cần phải FindNext đó chính là After:=NextRange. Với Find Method thì cách hoạt động của nó rất giống với Range ở điểm, nếu ta không xác định tên sheet, thì xem như địa chỉ là sheet hiện hành; vì thế với Find ta cũng phải xác định cho nó vùng nào là vùng tham chiếu, điều kiện nào là điều kiện dò tìm thì nó mới đảm bảo thực hiện được yêu cầu của mình, nếu không, nó lấy kết quả của lần tìm kiếm cuối cùng làm điều kiện What cho lần tiếp theo.

Chỗ đỏ đỏ ... bó tay

Find là phương thức của range. Nếu ta gọi rng.find trong đó rng là range thì range đó là xác định trên sheet xác định rồi chứ sao lại là sheet hiện hành hay không hiện hành? Cho dù đang mở 1000 tập tin và mỗi tập tin có 1000 sheet thì cái rng kia là xác định và duy nhất.

1. Sheet1 là sheet hiện hành

2. Code
Mã:
Set rng = Sheet2.Range("A1:A12")
Set xyz = rng.Find(...)

Làm gì có chuyện "nếu ta không xác định tên sheet", vì sao lại phải "xác định" tên Sheet? rng là Range xác định trên Sheet xác định chứ làm gì có thể là Range trên Sheet khác Sheet2? Cũng làm gì có chuyện "thì xem như địa chỉ là sheet hiện hành"? Sheet2 đâu phải là Sheet hiện hành?

Khi ta gọi phương thức Find là ta đã gọi phương thức Find cho/của Range xác định. Và Range đó nằm trên Sheet xác định của tập tin xác định. Range đó là xác định và duy nhất, vậy sao lại phải "nếu ta không xác định tên sheet" hay ""nếu ta xác định tên sheet""?

Cũng chả phải "vì thế với Find ta cũng phải xác định cho nó vùng nào là vùng tham chiếu" Find đâu có phải là một hàm kiểu như các hàm COPUNT, SUM đâu mà phải xác định cho nó vùng tham chiếu, tức truyền tham số vào? Find là phương thức của Range. Vậy nếu ta gọi Find của Range rng, tức rng.Find thì vùng tìm là rng, nếu gọi phương thức của range rng2, tức rng2.Find thì vùng tìm là rng2. Thế thôi chứ "xác định cho nó" là nghĩa gì?
 
Upvote 0
Vậy theo các Thầy, em làm như thế này có bị gọi là kỳ kỳ không?

Mã:
Sub TEST()
    Dim rngXuat As Range, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    Sheet1.Range("B3:B8") = "IN USED"
    MsgBox "Restore OK!"
    
    Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
    If rngXuat Is Nothing Then Exit Sub
    fstRng = rngXuat.Address
    
    Do
        Sheet1.Range("A3:A18").Find(What:=rngXuat.Offset(, -1), _
                                    LookIn:=xlFormulas, _
                                    LookAt:=xlWhole).Offset(, 1).ClearContents
                                    
        Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                                  After:=rngXuat, _
                                                  LookIn:=xlFormulas, _
                                                  LookAt:=xlWhole)
        If rngXuat Is Nothing Then Exit Do
        
    Loop Until rngXuat.Address = fstRng
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Vậy theo các Thầy, em làm như thế này có bị gọi là kỳ kỳ không?

Mã:
Sub TEST()
    Dim rngXuat As Range, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    Sheet1.Range("B3:B8") = "IN USED"
    MsgBox "Restore OK!"
    
    Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
    If rngXuat Is Nothing Then Exit Sub
    fstRng = rngXuat.Address
    
    Do
        Sheet1.Range("A3:A18").Find(What:=rngXuat.Offset(, -1), _
                                    LookIn:=xlFormulas, _
                                    LookAt:=xlWhole).Offset(, 1).ClearContents
                                    
        Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                                  After:=rngXuat, _
                                                  LookIn:=xlFormulas, _
                                                  LookAt:=xlWhole)
        If rngXuat Is Nothing Then Exit Do
        
    Loop Until rngXuat.Address = fstRng
End Sub
Trong code này em thấy một số vấn đề sau:
1. Thiếu câu lệnh On Error Resume Next, dẫn đến sẽ báo lỗi ở câu lệnh Set rngXuat... đầu tiên nếu không tìm thấy, tương tự như vậy với câu lệnh ClearContents.
2. Câu lệnh If rngXuat Is Nothing Then Exit Do là thừa vì điều kiện của If không thể xảy ra trong trường hợp này, nếu xảy ra thì nó đã Exit Sub từ trên rồi, ít ra trong trường hợp này thì rngXuat là ô đầu tiên tìm thấy (nếu chỉ có 1 ô thỏa mãn).
3. Em có thắc mắc nhỏ là tại sao không sử dụng xlValues mà lại là xlFormulas trong các tham số của Find?
 
Upvote 0
Trong code này em thấy một số vấn đề sau:
1. Thiếu câu lệnh On Error Resume Next, dẫn đến sẽ báo lỗi ở câu lệnh Set rngXuat... đầu tiên nếu không tìm thấy, tương tự như vậy với câu lệnh ClearContents.
2. Câu lệnh If rngXuat Is Nothing Then Exit Do là thừa vì điều kiện của If không thể xảy ra trong trường hợp này, nếu xảy ra thì nó đã Exit Sub từ trên rồi, ít ra trong trường hợp này thì rngXuat là ô đầu tiên tìm thấy (nếu chỉ có 1 ô thỏa mãn).
3. Em có thắc mắc nhỏ là tại sao không sử dụng xlValues mà lại là xlFormulas trong các tham số của Find?
1) Không cần On Error Resume Next, bởi ngoài vòng lặp ta đã dùng thủ tục này:

Mã:
    Set [COLOR=#008000][B]rngXuat [/B][/COLOR]= Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
[COLOR=#ff0000][B]    If rngXuat Is Nothing Then Exit Sub[/B][/COLOR]
[B][COLOR=#0000ff]    fstRng = rngXuat.Address[/COLOR][/B]
Vì thế, khi nó lọt qua "lưới" màu đỏ là nó sẽ không bao giờ gặp lỗi Error 91.

Ta để Find này ở ngoài là vì chỗ màu xanh làm cái "hang thoát thân" nếu quay vòng lại, đồng thời cũng là nguồn cung cấp cho cái After:=rngXuat của Find trong vòng lặp.

2) Trong trường hợp code này thì nó sẽ thừa, nhưng nếu ta thêm câu lệnh:

Mã:
rngXuat.Offset(, -1).Resize(, 2).ClearContents

thì không có nó sẽ vướng lỗi Error 91.

3) Như đã đề cập ở topic "Đố vui căn bản", anh đã có nói vấn đề này rồi (#41), khi ta dùng xlFormulas sẽ nhanh hơn xlValues khi ta tìm kiếm với "mật độ" dày đặc điều kiện có trong vùng dữ liệu.
 
Lần chỉnh sửa cuối:
Upvote 0
1) Không cần On Error Resume Next, bởi ngoài vòng lặp ta đã dùng thủ tục này:

Mã:
    Set [COLOR=#008000][B]rngXuat [/B][/COLOR]= Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
[COLOR=#ff0000][B]    If rngXuat Is Nothing Then Exit Sub[/B][/COLOR]
[B][COLOR=#0000ff]    fstRng = rngXuat.Address[/COLOR][/B]
Vì thế, khi nó lọt qua "lưới" màu đỏ là nó sẽ không bao giờ gặp lỗi Error 91.

Ta để Find này ở ngoài là vì chỗ màu xanh làm cái "hang thoát thân" nếu quay vòng lại, đồng thời cũng là nguồn cung cấp cho cái After:=rngXuat của Find trong vòng lặp.

2) Trong trường hợp code này thì nó sẽ thừa, nhưng nếu ta thêm câu lệnh:

Mã:
rngXuat.Offset(, -1).Resize(, 2).ClearContents

thì không có nó sẽ vướng lỗi Error 91.

3) Như đã đề cập ở topic "Đố vui căn bản", anh đã có nói vấn đề này rồi (không biết nó có còn tồn tại hay không), khi ta dùng xlFormulas sẽ nhanh hơn xlValues khi ta tìm kiếm với "mật độ" dày đặc điều kiện có trong vùng dữ liệu.
1. Đồng ý với anh vấn đề đặt một câu lệnh Set rngXuat =... ở ngoài vòng lặp, em cũng thường làm như vậy, và hình như ai cũng làm như vậy, nhưng chắc chắn rằng chính câu lệnh này sẽ gặp lỗi nếu trong vùng B3:B12 của Sheet2 không chứa giá trị cần tìm. Chính vì vậy mà em đề xuất thêm câu lệnh On Error Resume Next vào đầu Sub.

2. Em thấy câu trả lời của anh chưa thuyết phục, vì khi "lọt lưới" ở câu lệnh Set rngXuat = ... ngoài vòng lặp thì chắc chắn rằng điều kiện rngXuat Is Nothing không thể xảy ra (nếu xảy ra thì nó đã Exit Sub rồi mà), vậy thì cớ gì câu lệnh rngXuat.Offset(, -1).Resize(, 2).ClearContents lại vướng lỗi?!

3. Về vấn đề này thì em sẽ kiểm chứng lại, vì thực sự em chưa để ý đến vấn đề này, chỉ hiểu vấn đề đơn giản: xlValues là tìm trong giá trị ô (nếu có công thức thì nó tìm trong kết quả của công thức đó), còn xlFormulas là tìm trong chính công thức của ô nếu ô đó chứa công thức. Như vậy nếu tìm chuỗi "ABC" thì ô chứa công thức =A1="ABC" sẽ là một ô thỏa mãn điều kiện tìm nếu ta chọn xlFormulas, và là một ô không thỏa mãn điều kiện tìm nếu ta chọn xlValues (tất nhiên là với cùng tùy chọn xlPart).
 
Upvote 0
1. Đồng ý với anh vấn đề đặt một câu lệnh Set rngXuat =... ở ngoài vòng lặp, em cũng thường làm như vậy, và hình như ai cũng làm như vậy, nhưng chắc chắn rằng chính câu lệnh này sẽ gặp lỗi nếu trong vùng B3:B12 của Sheet2 không chứa giá trị cần tìm. Chính vì vậy mà em đề xuất thêm câu lệnh On Error Resume Next vào đầu Sub.

2. Em thấy câu trả lời của anh chưa thuyết phục, vì khi "lọt lưới" ở câu lệnh Set rngXuat = ... ngoài vòng lặp thì chắc chắn rằng điều kiện rngXuat Is Nothing không thể xảy ra (nếu xảy ra thì nó đã Exit Sub rồi mà), vậy thì cớ gì câu lệnh rngXuat.Offset(, -1).Resize(, 2).ClearContents lại vướng lỗi?!

3. Về vấn đề này thì em sẽ kiểm chứng lại, vì thực sự em chưa để ý đến vấn đề này, chỉ hiểu vấn đề đơn giản: xlValues là tìm trong giá trị ô (nếu có công thức thì nó tìm trong kết quả của công thức đó), còn xlFormulas là tìm trong chính công thức của ô nếu ô đó chứa công thức. Như vậy nếu tìm chuỗi "ABC" thì ô chứa công thức =A1="ABC" sẽ là một ô thỏa mãn điều kiện tìm nếu ta chọn xlFormulas, và là một ô không thỏa mãn điều kiện tìm nếu ta chọn xlValues (tất nhiên là với cùng tùy chọn xlPart).
1) Nhắc mới nhớ, là còn một Find thứ 2 chưa bẫy lỗi:

Mã:
Sub TEST()
    Dim rngNhap As Range, rngXuat As Range, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    Sheet1.Range("B3:B8") = "IN USED"
    MsgBox "Restore OK!"
    
    Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
    If rngXuat Is Nothing Then Exit Sub
    fstRng = rngXuat.Address
    
    Do
        MsgBox rngXuat.Address
        
[COLOR=#0000ff]        Set rngNhap = Sheet1.Range("A3:A18").Find(What:=rngXuat.Offset(, -1), _[/COLOR]
[COLOR=#0000ff]                                    LookIn:=xlFormulas, _[/COLOR]
[COLOR=#0000ff]                                    LookAt:=xlWhole)[/COLOR]

[COLOR=#0000ff]        If Not rngNhap Is Nothing Then[/COLOR]
[COLOR=#0000ff]            rngNhap.Offset(, 1).ClearContents[/COLOR]
[COLOR=#0000ff]        End If[/COLOR]
                                    
[COLOR=#ff0000]        rngXuat.Offset(, -1).Resize(, 2).ClearContents[/COLOR]
        
        Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
[COLOR=#008080]                                                  After:=rngXuat, _[/COLOR]
                                                  LookIn:=xlFormulas, _
                                                  LookAt:=xlWhole)
[COLOR=#800080]        If rngXuat Is Nothing Then Exit Do[/COLOR]
        
    Loop Until rngXuat.Address = fstRng
End Sub

Thường là lỗi bất khả kháng anh mới xài On Error Resume Next, còn không thì phải bẫy lỗi cho kỳ hết mới thôi, dĩ nhiên phải test nhiều tập.

2) Dòng màu đỏ xuất hiện, dòng màu tím bẫy cho nó. Nguyên tắc là, nếu không tìm thấy điều kiện nào thì nó thoát từ vòng loại là OK; tuy nhiên nếu vùng dữ liệu có chứa từ 2 điều kiện trở lên thì nó sẽ phát sinh ra lỗi tại dòng rngXuat.Address = fstRng. Cũng dễ hiểu thôi, nó đã xóa dòng điều kiện rồi thì rngXuat trở thành Nothing thì làm sao có được địa chỉ phải không?

3) nghiaphuc thử luôn trên cái file ở bài này (#1) sẽ biết liền chớ gì!

Em thử copy một ô có giá trị mà không có công thức, em paste special, chọn Formula thì nó ra kết quả là gì? Rồi từ đó em hiểu về thằng Find này. Dĩ nhiên ta phải biết rõ dữ liệu của ta có chứa công thức hay không mới có thể tìm kiếm được với kiểu nào là thuận tiện.

Với xlValues nó sẽ lấy giá trị dù có là công thức hay không, với xlFormula, nó chỉ lấy giá trị và loại trừ giá trị là kết quả từ công thức (có lẽ vì điều này mà nó nhanh hơn chăng?).
 
Lần chỉnh sửa cuối:
Upvote 0
Mình có đoạn code sau:

Dim Data, Arr, Result(), RngTV As Range, FCll1 As Range, FCll2 As Range, FindRng As Range, i As Long, j As Long
Data = Sheet1.Range(Sheet1.[D2], Sheet1.[A65536].End(xlUp))
Set RngTV = Sheet2.Range(Sheet2.[A4], Sheet2.[A65536].End(xlUp))
For i = 1 To UBound(Data, 1)
Set FCll1 = RngTV.Offset(, 2).Find(Data(i, 3), RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)
Các bạn cho mình hỏi dòng bôi đỏ và in đậm được hiểu là đang tìm kiếm điều gì! mình so sánh với phương thức FInd được trình bày trong chủ đề mà vẫn chưa hiểu. Mong được sự giúp đở các bạn!
 
Upvote 0
Mình có đoạn code sau:

Dim Data, Arr, Result(), RngTV As Range, FCll1 As Range, FCll2 As Range, FindRng As Range, i As Long, j As Long
Data = Sheet1.Range(Sheet1.[D2], Sheet1.[A65536].End(xlUp))
Set RngTV = Sheet2.Range(Sheet2.[A4], Sheet2.[A65536].End(xlUp))
For i = 1 To UBound(Data, 1)
Set FCll1 = RngTV.Offset(, 2).Find(Data(i, 3), RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)
Các bạn cho mình hỏi dòng bôi đỏ và in đậm được hiểu là đang tìm kiếm điều gì! mình so sánh với phương thức FInd được trình bày trong chủ đề mà vẫn chưa hiểu. Mong được sự giúp đở các bạn!
Câu lệnh của Range.Find Method nó chỉ như vầy thôi:

expression.Find(What, After, LookIn, LookAt, SearchOrder, SearchDirection, MatchCase, MatchByte, SearchFormat)

Nhóm có màu xanh tôi không có file nên không biết nó chứa gì trong đó và nó đại diện cho cái gì của After.
 
Upvote 0
vâng đó là cú pháp em vẫn nghĩ! nhưng phương thức FIND của hàm này khiến em đau cả đầu. Thực ra đây là 1 đoạn code trong cách "diễn giải nhật ký thi công " trên diễn đàn của anh Huuthang_bd. đoạn code đem lại hiệu quả rất tốt, nhưng để hiểu đoạn code em không tài nào giải thích được. các bạn có ý kiến gì khác hơn để giải thích đoạn code được không! Mình sẽ đính kèm file nhật ký có đoạn code đầy đủ.
 

File đính kèm

  • Nhat ky cong trinh.rar
    21.8 KB · Đọc: 47
Lần chỉnh sửa cuối:
Upvote 0
PHP:
Dim Data, Arr, Result(), RngTV As Range, FCll1 As Range, FCll2 As Range, FindRng As Range, i As Long, j As Long


Data = Sheet1.Range(Sheet1.[D2], Sheet1.[A65536].End(xlUp))
Set RngTV = Sheet2.Range(Sheet2.[A4], Sheet2.[A65536].End(xlUp))
For i = 1 To UBound(Data, 1)
     Set FCll1 = RngTV.Offset(, 2).Find(Data(i, 3), RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)
. . . . 
Next i
Để tìm hiểu câu lệnh màu đỏ đó là gì, ta cần hiểu rõ đâu là giới hạn của vùng RngTV.Offset(, 2)
Theo như câu lệnh trong đó, thì vùng xuất fát điểm của vùng này sẽ là RngTV;
Bản thân RngTV được diễn dịch như sau:
Đó là vùng dữ liệu thuộc cột [A] của trang tính có tên là ‘Sheet2’ bắt đầu từ [A4]
Vậy RngTV.Offset(, 2) sẽ là vùng dữ liệu cột [A] nêu trên được dịch chuyển sang fải 2 cột (nghĩa là cột [C]) cho dù vùng ở cột [C] này có dữ liệu hay không.
Đó là ta đã xác định vùng cần dò tìm;

Tiếp tục, ta sẽ fải xác định cần tìm cái gì trong vùng này;
Điều này thể hiện trong câu lệnh là Data(i,3)
Mà theo như câu lệnh đầu (không kể các mệnh đề định nghĩa biến trên nó), thì đây là 1 biến mảng đang chứa dữ liệu tại cột [A] bắt đầu từ [A2] cho đến ô cuối của cột này có chứa dữ liệu & mở rọng qua fải đến cột [D]
( Cú fáp này ta có thể viết khác đi chút, nhưng chưa nên nói tới ở đây)

Tiếp theo sẽ khảo sát vùng bắt đầu tìm, đó là vùng
RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2)
Thú thực mình chưa thể hiểu cái lằng nhằng này là thứ ôn dịch gì !?!
Thông thường tham số này hãn hữu người ta mới xài; còn ở đây muốn hiểu nó cần có file (như HT Nghĩa đã nêu)
Để đi tiếp ta có thể bỏ qua tham biến này (bằng cách là xóa nó đi trong câu lệnh)
Còn các tham biến còn lại theo thứ tự bạn có thể tìm trong bài của Hoàng Danh ở đâu topic.
Nhưng mình xin được bày tỏ là các tham biến này mình luôn xài của VBA đề xuất, như xlFormulas, xlPart,. .. .& mình chúa ghét cách xài như trên. (& bỡi ghép nên chỉ có lời khuyên với bạn là muốn tường tận về nó, bạn cần mở cửa số ‘Immediate’ trong VBE để có thêm trợ giúp khi cần.

Chúc vui trong ngày làm việc đầu tuần.
 
Upvote 0
Mặc dù vẫn chưa sáng tỏ hết cả câu lệnh, nhưng cũng cảm ơn bạn rất nhiều vì đã đóng góp ý kiến. Xin cậy nhờ thêm các cao thủ ra tay giúp với. Nói thật câu lệnh này làm mình đau hết cả đầu mấy tháng nay! Không biết tác giả có theo dõi không xin ra tay chỉ giáo.
 
Upvote 0
Một cách khác bạn có thể tận dụng, đó là hộp thoại MsgBox, như:

Trước hay sau dòng lệnh này ta xài 3 hộp thoại, như
PHP:
'
Set FCll1 = RngTV.Offset(, 2).Find(Data(i, 3), RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)
'
 MsgBox RngTV.Offset(, 2).Address
 MsgBox Data(i, 3)
 MsgBox RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2).Address
 
Upvote 0
Web KT
Back
Top Bottom