Gỡ rỗi function lọc số hoá đơn và ngày trong chuỗi

Liên hệ QC

hondatron

Thành viên mới
Tham gia
14/9/10
Bài viết
49
Được thích
3
Hỏi cách tách ngày tháng năm trong một chuỗi.

Cảm ơn sự góp ý của 2 anh VietMini và anh Swim. Có lẽ em bị rối trong đặt câu hỏi nên em xoá toàn bộ dung topic cũ. Và đặt lại câu hỏi lại. Có gì mong các anh quản trị thông cảm dùm em.
Cụ thể:
ví dụ em có 1 đoạn text: Sửa máy photocopy số hoá đơn 000123 ngày 15/12/2008 phòng chăm sóc sức khoẻ.

- Em muốn lấy: 15/12/2008
- nội dung "Sửa máy photocopy số hoá đơn 000123 ngày" va "phòng chăm sóc sức khoẻ." thay đổi theo từng lần phát sinh.
- Em muốn nhờ các anh có thể cho em 1 đoạn code vba để sử lý tình huống này với ah.
 
Lần chỉnh sửa cuối:
Gỡ cái gì? nếu là lỗi thì cho biết nó báo lỗi gì, chỗ nào; nếu chạy sai thì cho biết một vài ví dụ đầu vào, đầu ra và đầu ra dự tính.

Ít nhất phải biết bạn muốn làm cái gì mới gỡ rối được.
 
Gỡ cái gì? nếu là lỗi thì cho biết nó báo lỗi gì, chỗ nào; nếu chạy sai thì cho biết một vài ví dụ đầu vào, đầu ra và đầu ra dự tính.

Ít nhất phải biết bạn muốn làm cái gì mới gỡ rối được.

Em xin rút kinh nghiệm và bổ sung.
 
nhờ mọi nguời giúp đỡ em xúi
 
Nói là function là thật đao to búa lớn quá, em thấy nó chỉ là 1 tổ hợp code, trong này em có mượn code của 1 số anh trong diễn đàn rồi biến đổi thêm.
PHP:
Function TachSo(Chuoi As String) As String
  Dim Temp1 As String, Temp2 As Variant, i As Integer
  Dim vitri As Integer, dodai As Double, ham As WorksheetFunction
  With CreateObject("VBScript.RegExp")
    .Global = True
    .Pattern = "[^0-9.\d{2}/\d{2}/\d{4}]"
    Temp1 = .Replace(Chuoi, " ") 
  End With
  Temp2 = Split(WorksheetFunction.Trim(Temp1), " ")
    For i = 0 To UBound(Temp2)
        
     If Len(Temp2(i)) = 7 Then
        vitri = i
      Exit For
    End If
      

    If Len(Temp2(i)) = 10 And InStr(1, Temp2(i), "/", 1) = 3 And InStr(3, Temp2(i), "/", 1) = 6 Then
   
        vitri = i
     Else: Temp2(i) = ""
     
        Exit For
        End If
      
    Next i
  
  TachSo = Temp2(vitri)
End Function

Em kiểm tra chuối ký tự dd/mm/yyyy nhưng không chạy được.
Với phần With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "[^0-9.\d{2}/\d{2}/\d{4}]"
Temp1 = .Replace(Chuoi, " ")
End With
Em không biết kiểu khai báo của em đã đúng chưa. Em muốn là lấy giá trị là số và lấy giá trị kiểu dd/dd/dddd.

Với dữ liệu theo kiểu ví dụ của bạn -->mình cũng làm một đoạn code tương ứng với ví dụ của bạn :
Mã:
Public Function Tach(Source) As String
    Dim str$
        str = CStr(Source)
        With CreateObject("vbscript.regexp")
            .global = true
            .Pattern = "[^0-9/]+"
            Tach = .Replace(str, "")
        End With
End Function
 
nhờ mọi nguời giúp đỡ em xúi

Tốt hơn hết bạn nên cho vd. về chuỗi và kết quả. Hãy miêu tả vấn đề.
Bạn cho code thì từ code đoán cái bạn dự định trong đầu rất khó. Vì code của bạn có thể sai. Mà từ code sai thì có thánh mới đoán được cái nằm trong đầu bạn. Ít ra cũng phải dò từng dòng code để đoán.

Có một chuỗi, bạn muốn lấy cái gì, tiêu chí là thế nào? Nếu có nhiều đoạn thỏa tiêu chí thì lấy cái đầu, cái cuối, lấy hết?

Trong ghi chú có thấy nói về ngày tháng nhưng trong tiêu đề có thêm "số hoá đơn". Nếu trong chuỗi không có "số hóa đơn" nọ thì đưa nó vào tiêu đề để làm gì cho tối nghĩa nội dung? Còn nếu có thì hãy miêu tả, hãy cho ví dụ cụ thể về chuỗi.

Mà nửa sau của code cũng không biết làm cái gì. Đúng là đầu óc mình hạn hẹp không hiểu được ý tưởng lớn.

Hỏi cũng không biết cách, không thì dọn đồ luôn cho khỏe, khỏi phải chờ đợi câu trả lời bạn ạ. Bạn VetMini đã nhắc mà bạn có làm đâu?
 
Cảm ơn anh Swi và anh VetMiNi đã có những góp ý quý giá cho em. Em đã sửa lại nội dung topic 1 cách cụ thể nhất. Nhờ các anh em xem hộ em.
 
Cảm ơn anh Swi và anh VetMiNi đã có những góp ý quý giá cho em. Em đã sửa lại nội dung topic 1 cách cụ thể nhất. Nhờ các anh em xem hộ em.

Bạn vẫn không trả lời câu hỏi của tôi

Nếu có nhiều đoạn thỏa tiêu chí thì lấy cái đầu, cái cuối, lấy hết?

Vậy tôi không hỏi nữa. Cho dù chuỗi có bao nhiêu ngày thì code chỉ lấy 1 kết quả đầu tiên

Mã:
Function ExtractDate(ByVal text As String) As String
Dim re As Object
     Set re = CreateObject("VBScript.RegExp")
     re.Pattern = "((?:0[0-9])|(?:[12][0-9])|(?:3[01]))[B][COLOR=#ff0000]/[/COLOR][/B]((?:0[1-9])|(?:1[0-2]))[B][COLOR=#ff0000]/[/COLOR][/B](?:19|20)\d\d"
     If re.test(text) Then ExtractDate = re.Execute(text).Item(0).Value
     Set re = Nothing
End Function

Nếu bạn muốn dùng trên sheet thì giả sử chuỗi tại A1.
Công thức cho vd. A2

Mã:
=extractdate(A1)

Nếu bạn muốn tìm cả khi ngày tháng có dạng (vd. dữ liệu từ nhiều nguồn, không đồng nhất) 15-08-2008 hoặc 15.08.2008 thì thay chỗ đỏ đỏ bằng [/-.] , tức <dấu gạch chéo><dấu trừ><dấu chấm>, tất cả trong ngoặc vuông
------
Hàm trên chỉ tính năm trong khoảng 1900 - 2099
 
Lần chỉnh sửa cuối:
dạ tại vâng, đoạn dd/mm/yyyy ở vị trí bất kỳ đúng rồi anh. Nhân tiện từ function của anh em muốn hỏi rộng hơn 1 xíu.
Em lấy lại ví dụ ở trên: "Sửa máy photocopy số hoá đơn 000123 ngày 15/12/2008 phòng chăm sóc sức khoẻ." Trong đó 0001234 không cố định và có độ dài là 7. Thì em thiết lập
"re.Pattern =" như thế nào anh.
 
dạ tại vâng, đoạn dd/mm/yyyy ở vị trí bất kỳ đúng rồi anh. Nhân tiện từ function của anh em muốn hỏi rộng hơn 1 xíu.
Em lấy lại ví dụ ở trên: "Sửa máy photocopy số hoá đơn 000123 ngày 15/12/2008 phòng chăm sóc sức khoẻ." Trong đó 0001234 không cố định và có độ dài là 7. Thì em thiết lập
"re.Pattern =" như thế nào anh.

Tôi không hiểu bạn nói gì. Thiết lập cái gì? Trong chuỗi của bạn có cả ngày tháng và số hóa đơn. Tôi nghĩ là bạn muốn lấy ngày tháng nên viết code lấy ngày tháng. Hàm ExtractDate như tên nó chỉ rõ là hàm lấy ngày tháng.
Bây giờ bạn muốn lấy cái gì?
Tôi nhắc lại lần nữa: hãy miêu tả dữ liệu và yêu cầu. Tôi không chơi trò cò cưa, đoán ý đồng đội, thông tin nhỏ giọt.
Bạn nói rõ thì tôi giúp bạn lần cuối, nếu không thì ta dừng ở đây.
 
Chào chú Switom,
Thắc mắc 1:
Ví dụ của cháu là: Với chuỗi : Chuyển tiền tiếp khách theo hoá đơn số 0001234

Kết quả cần tìm: 0001234 ( Đây là dãy số gồm có độ dài là 7), nếu có nhiều đoạn thỏa tiêu chí thì lấy cái đầu.

Thắc mắc thứ 2:

Trong code: re.Pattern = "((?:0[0-9])|(?:[12][0-9])|(?:3[01]))/((?:0[1-9])|(?:1[0-2]))/(?:19|20)\d\d"

cháu chưa hiểu ?: mang ý nghĩa là gì.
Theo cháu nghĩ ?:0[0-9] là lấy giá trị bằng 0
?:[12][0-9] là lấy giá trị bằng 12, hiểu như thế đúng không chú
\d\d mang ý nghĩa là gì vậy ah,
cháu thấy trong hướng dẫn của chú về VBscrip thì
\d Có nghĩa là chữ số, tương đương với [0-9]. Được phép dùng trong […] nhưng \d\d thì cháu không hiểu.
 
Lần chỉnh sửa cuối:
Chào chú Switom,
Thắc mắc 1:
Ví dụ của cháu là: Với chuỗi : Chuyển tiền tiếp khách theo hoá đơn số 0001234

Kết quả cần tìm: 0001234 ( Đây là dãy số gồm có độ dài là 7), nếu có nhiều đoạn thỏa tiêu chí thì lấy cái đầu.

Thắc mắc thứ 2:

Trong code: re.Pattern = "((?:0[0-9])|(?:[12][0-9])|(?:3[01]))/((?:0[1-9])|(?:1[0-2]))/(?:19|20)\d\d"

cháu chưa hiểu ?: mang ý nghĩa là gì.
Theo cháu nghĩ ?:0[0-9] là lấy giá trị bằng 0
?:[12][0-9] là lấy giá trị bằng 12, hiểu như thế đúng không chú
\d\d mang ý nghĩa là gì vậy ah,
cháu thấy trong hướng dẫn của chú về VBscrip thì
\d Có nghĩa là chữ số, tương đương với [0-9]. Được phép dùng trong […] nhưng \d\d thì cháu không hiểu.

1. Về 0[0-9]
0 là ký tự 0
[0-9] là 1 ký tự trong tập 0, 1, 2, ..., 9
Vậy 0[0-9] là 2 ký tự mà trong đó ký tự đầu là 0 còn ký tự thứ 2 là 1 ký tự trong tập 0, 1, 2, ..., 9
Tức các số 00, 01, 02, ..., 09 khớp với 0[0-9]

Có thể dùng (0[0-9]) nhưng lúc đó các đoạn khớp sẽ được ghi nhớ, tức tốn điện nước. Còn viết (?:0[0-9]) thì các đoạn khớp không được ghi nhớ, tức không tốn điện nước.
Tổng quát (?:pattern) thì các đoạn khớp với pattern sẽ không được ghi nhớ, tức không tốn điện nước.

Trên cơ sở đó thì
[12][0-9] là 2 ký tự mà ký tự 1 là 1 hoặc 2 còn ký tự thứ 2 là 0, 1, 2, ... hoặc 9, tức 10, 11, ..., 19, 20, 21, ..., 29 đều khớp

3[01] là 2 ký tự mà ký tự 1 là 3 còn ký tự thứ 2 là 0 hoặc 1, tức 30, 31 đều khớp

Vậy
(?:0[0-9])|(?:[12][0-9])|(?:3[01]) = (0[0-9])|([12][0-9])|(3[01]) = 0[0-9] hoặc [12][0-9] hoặc 3[01] tức
00, 01, 02, ..., 09 hoặc 10, 11, ..., 19, 20, 21, ..., 29 hoặc 30, 31 đều khớp
tức 00, 01, ..., 09, 10, 11, ..., 19, 20, 21, ..., 29, 30, 31 đều khớp tức 00, 01, ..., 31 đều khớp.

2. Về \d\d
\d là 1 chữ số từ tập 0, 1, ..., 9. Vậy thì \d\d là 2 chữ số, thế thôi.
\d\d = \d{2}

Cần nói thêm là code chỉ có nhiệm vụ lọc dữ liệu thôi. Còn nhiệm vụ của dữ liệu là phải ĐÚNG. Tức dữ liệu không có kiểu vd.31/02/2014

Nhưng nếu chắc chắn ngoài ngày tháng ra thì trong chuỗi không còn cái gì khác có dạng **/**/**** (* là chữ số) thì cũng có thể dùng pattern đơn giản: pattern = "\d{2}/\d{2}/\d{4}"

Tôi nhắc lại là dùng pattern trên chỉ khi ngoài ngày tháng ra thì trong chuỗi không còn cái gì khác có dạng **/**/****. Ví dụ không có 94/12/2004 QĐ - Quyết định (của Bộ trưởng) số 94 ra tháng 12 năm 2004 (he he)

Còn nếu có thể có **/**/**** mà không là ngày tháng thì phải sửa code.

--------------
Thắc mắc 1:
Lấy code y như bài trước nhưng Pattern = "\d{7}"
 
Con cảm ơn chú, con đã hiểu bài.
 
chú Switom giúp con trường hợp này với.

ví dụ với đoạn test:
Đoạn 1: qwerrtttt 19/03/2014 abcxyz 21/03/2014 xccca 24/05/2014
---> Kết quả: 21/03/2014
Đoạn 2: qwerrtttt 19/03/2014 abcxyz ----> Kết quả 19/03/2014

Tiêu chí: Có nhiều đoạn thỏa tiêu chí thì lấy cái thứ 2, còn có 1 đoạn thì lấy cái đầu tiên.

Cháu có lấy ví dụ chú cho cháu và bổ sung thêm, nhưng kết quả không mong muốn. Nhờ chú chỉnh giúp cháu với ah.


Mã:
Function ExDate(ByVal text As String) As String
Dim re As Object, i As Integer, temp As Variant
     Set re = CreateObject("VBScript.RegExp")
     re.Pattern = "((?:0[0-9])|(?:[12][0-9])|(?:3[01]))/((?:0[1-9])|(?:1[0-2]))/(?:19|20)\d\d"
  
    IIf re.Test(text) And re.Execute(text).Item(1).Value > "", ExDate = re.Execute(text).Item(1).Value, ExDate = re.Execute(text).Item(0).Value
      
     Set re = Nothing
End Function
 
chú Switom giúp con trường hợp này với.

ví dụ với đoạn test:
Đoạn 1: qwerrtttt 19/03/2014 abcxyz 21/03/2014 xccca 24/05/2014
---> Kết quả: 21/03/2014
Đoạn 2: qwerrtttt 19/03/2014 abcxyz ----> Kết quả 19/03/2014

Tiêu chí: Có nhiều đoạn thỏa tiêu chí thì lấy cái thứ 2, còn có 1 đoạn thì lấy cái đầu tiên.

Cháu có lấy ví dụ chú cho cháu và bổ sung thêm, nhưng kết quả không mong muốn. Nhờ chú chỉnh giúp cháu với ah.


Mã:
Function ExDate(ByVal text As String) As String
Dim re As Object, i As Integer, temp As Variant
     Set re = CreateObject("VBScript.RegExp")
     re.Pattern = "((?:0[0-9])|(?:[12][0-9])|(?:3[01]))/((?:0[1-9])|(?:1[0-2]))/(?:19|20)\d\d"
  
    IIf re.Test(text) And re.Execute(text).Item(1).Value > "", ExDate = re.Execute(text).Item(1).Value, ExDate = re.Execute(text).Item(0).Value
     
     Set matches = Nothing  
     Set re = Nothing
End Function

Trước hết muốn tìm NHIỀU kết quả thì phải có Global = TRUE (mặc định là FALSE)
Mã:
Function ExtractDate(ByVal text As String) As String
Dim re As Object, matches As Object
     Set re = CreateObject("VBScript.RegExp")
     re.Global = True
     re.Pattern = "((?:0[0-9])|(?:[12][0-9])|(?:3[01]))/((?:0[1-9])|(?:1[0-2]))/(?:19|20)\d\d"
     If re.test(text) Then
        Set matches = re.Execute(text)
        If matches.Count > 1 Then
            ExtractDate = matches.Item(1).Value
        Else
            ExtractDate = matches.Item(0).Value
        End If
     End If
     Set matches = Nothing
     Set re = Nothing
End Function

Trong trường hợp hoặc lấy thứ 2 hoăc lấy thứ 1 thì cũng có thể
Mã:
Function ExtractDate(ByVal text As String) As String
Dim re As Object, matches As Object
     Set re = CreateObject("VBScript.RegExp")
     re.Global = True
     re.Pattern = "((?:0[0-9])|(?:[12][0-9])|(?:3[01]))/((?:0[1-9])|(?:1[0-2]))/(?:19|20)\d\d"
     If re.test(text) Then
        Set matches = re.Execute(text)
        ExtractDate = matches.Item(Abs(matches.Count > 1)).Value
     End If
     Set matches = Nothing
     Set re = Nothing
End Function

Do Abs(matches.Count > 1) trả về 0 (matches.Count > 1 = FALSE) hoặc 1 (matches.Count > 1 = TRUE)
Ta dùng ABS do TRUE = -1 (trong VBA)

Nhưng tôi khuyên nên dùng code đầu tiên.
 
Lần chỉnh sửa cuối:
Web KT

Bài viết mới nhất

Back
Top Bottom