Lọc dữ liệu và gán chuỗi trước số thuê bao (2 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

Sau khi được sự trợ giúp của mọi người ở bài #1 tôi đã hoàn thành được bước một công việc của mình.
Để khỏi loãng topic tôi xin gửi lên đây dữ liệu tiếp theo mà trước đó thầy siwtom và bạn hungpecc1 đã giúp tôi hoàn thành
Vì dữ liệu lần này lớn hơn nên tôi chỉ lấy ID của 4 trạm "<!bth1;<!bth2;<!bth3;<!cdc1;".
Yêu cầu lọc dữ liệu như sau:
1.Thống kê bằng excel ở cột bên cạnh trạm bth1,bth2... có bao nhiêu số thuê bao?
2.Thống kê có bao nhiêu dịch vụ tbo-1 và tbo-2 ở trong trạm đó.
Mong mọi người hướng dẫn và trợ giúp. Xin cảm ơn.
Sau đây là file đính kèm:

Bài này cấu trúc dữ liệu là hoàn toàn khác bài trước, đúng không?

Lần sau bạn nhớ miêu tả dữ liệu. Mình nói vài câu thì người khác không phải đoán, chỉ làm mà không cần suy nghĩ.

Bạn nói: tính số thuê bao. Vậy dữ liệu của mỗi thuê bao bắt đầu bằng cái gì và kết thúc bằng gì? Mỗi trạm bắt đầu bằng gì và kết thúc bằng gì?

Tôi làm với cách hiểu như sau:

1. Mỗi trạm, trừ trạm cuối, bắt đầu bằng <!xyz; và kết thúc khi gặp <!xyz; tiếp theo. Trạm cuối bắt đầu bằng <!xyz; và kết thúc khi dữ liệu kết thúc

2. Trong mỗi trạm thì mỗi thuê bao bắt đầu bằng <suscp:snb= và kết thúc bằng END
---------------

Bạn hãy kiểm tra lại. Code trả về 16 kết quả (A2:D5) nhưng có 2 kết quả không bằng kết quả bạn liệt kê. Tức của bạn là

!bth1; Thuê bao 1980
!bth2; Thuê bao 1789

code trả về

!bth1; Thuê bao 1982
!bth2; Thuê bao 1795

Tôi ngại kiểm tra quá

Mã:
Sub DoSomething()
    Dim fso As Object, re As Object, Match As Object, oMatches As Object
    Dim FileName, Arr(), i As Long, s As String
        
        FileName = Application.GetOpenFilename()
        If FileName <> "False" Then
            Set fso = CreateObject("Scripting.FileSystemObject")
            With fso.OpenTextFile(FileName)
                s = .ReadAll
                .Close
            End With
            Set fso = Nothing
            Set re = CreateObject("VBScript.RegExp")
            With re
                .Global = True
                .Pattern = "<(!.+;)(?:\n|.)+?(?=(?:<!.+;|$))"
                Set Match = .Execute(s)
                If Not Match Is Nothing Then
                    ReDim Arr(1 To Match.count, 1 To 4)
                    
                    For i = 0 To Match.count - 1
                        Arr(i + 1, 1) = Match(i).SubMatches(0)
                        
                        .Pattern = "\d{9}\s"
                        Set oMatches = .Execute(Match(i))
                        If Not oMatches Is Nothing Then Arr(i + 1, 2) = oMatches.count
                        
                        .Pattern = "\sTBO-1\s"
                        Set oMatches = .Execute(Match(i))
                        If Not oMatches Is Nothing Then Arr(i + 1, 3) = oMatches.count
                        
                        .Pattern = "\sTBO-2\s"
                        Set oMatches = .Execute(Match(i))
                        If Not oMatches Is Nothing Then Arr(i + 1, 4) = oMatches.count
                    Next
                    [A2].Resize(UBound(Arr), 4) = Arr
                End If
            End With
        End If
End Sub
 

File đính kèm

Lần chỉnh sửa cuối:
Thế thì tôi chả hiểu nữa. Cấu trúc dữ liệu của bạn thế nào thì tự bạn phải biết chứ sao lại tôi nói như thế nào thì bạn cũng "gật"? Tôi chỉ đoán là mỗi trạm bắt đầu bằng <!xyz; và kết thúc bằng END. Còn thực sự cấu trúc thế nào thì bạn phải nói cho tôi biết chứ?

Nhìn vào dữ liệu ở bài #19 thì tôi bó tay rồi. Trước đó tôi nghĩ là mỗi trạm bắt đầu bằng <!xyz; và khi gặp END thì kết thúc trạm đó. Nhưng xem dữ liệu ở bài #19 thì sau <!bth1; có hàng trăm END

Bài #19 là bài hoàn toàn khác?

Không phải là em không hiểu cấu trúc dữ liệu của mình, mà vì mới tìm hiểu về excel nên em không hiểu cấu trúc code VBA thôi. Bài #17 thầy đã giúp em giải quyết được vấn đề, dữ liệu chưa chuẩn em đã chỉnh sửa lại chuẩn rồi, bằng cách thêm vào !xyz'; !xyz'';... cho cân bằng số END. Và mỗi trạm mình có thể hiểu bắt đầu bằng <!xyz; và kết thúc bằng END.
Bài #19 sau <!bth1; có hơn nghìn END thầy ạ.
1.Để giải quyết vấn đề em nhờ thầy giúp em chỉnh lại code bài #17 là thêm dòng !EXECUTED; cuối mỗi !xyz; Tức là:
!xyz;
(các số thuê bao)
!EXECUTED;
2. Bài #19 dữ liệu sẽ là:
<!xyz;
<suscp:snb=3872000;
SUBSCRIBER DATA
SNB DEV DETY SUT SCL MIS COS
383872000 LIMA-12780
END

<!EXECUTED;
3. Kết thúc vòng lặp sẽ là: <!xyz; <!EXECUTED;
Em đính kèm file có dòng <!EXECUTED; cuối mỗi trạm không biết vấn đề có phức tạp thêm không ạ? Mong chờ ý kiến của thầy.
 
Em mải đọc bài không biết thầy đã làm xong. Để em test lại bài đã, chân thành cảm ơn thầy.
 
Dạ, dữ liệu bài này cấu trúc có khác bài trước. Bài trước là hiển thị tình trạng thuê bao (IDLE, BUSY, BLOC...), bài sau là hiển thị đặc tính thuê bao (TBO-1, TBO-2...)
Để đơn giản em chỉ yêu cầu thêm "n=" để hiểu là number
PHP:
Arr(count) = "n=" & oMatches(r).SubMatches(0) & ";"
Sau khi có code em sửa lại thành
PHP:
Arr(count) = "suscp:snb=" & oMatches(r).SubMatches(0) & ";"
Code của thầy em đã cho chạy một file khác lớn hơn cho ra kết quả rất đúng với kết quả của em.
Kết quả của em là dùng một cách củ chuối, sử dụng chức năng replace
!bth1; Thuê bao 1980
!bth2; Thuê bao 1789
code trả về:
!bth1; Thuê bao 1982
!bth2; Thuê bao 1795
Chỉ sai 2/90 trạm, dữ liệu của em bị lỗi ở 2 trạm này.
Một lần nữa em chân thành cảm ơn thầy. Chúc thầy mạnh khoẻ.
 
Mỗi bài tôi cho bạn 2 phiên bản. Chọn phiên bản nào là tùy bạn
-----------------
Ở bài trước bạn có nói là bạn tự sửa lại dữ liệu chưa chuẩn. Nhưng đã code thì để cho nó làm max việc còn mình có max thời gian uống cà phê.

Bây giờ tôi cho bạn phiên bản 2 của bài trước. Những dữ liệu sai thì không được xử lý và chúng sẽ được ghi sang tập tin mới có đuôi là: <tên tập tin cũ>_error.log

Như vậy tập tin sẽ nhỏ đi rất nhiều. Và trạm đầu tiên có ngay. Các trạm tiếp theo tìm bằng cách tìm END liên tiếp thì các dòng sau END sẽ là chỗ điền tên trạm.

Mã:
Sub DoSomething()
    Dim fso As Object, re As Object, Match As Object, oMatches As Object, r As Long, count As Long
    Dim FileName, Arr() As String, i As Long, s As String, result() As String
        FileName = Application.GetOpenFilename()
        If FileName <> "False" Then
            Set fso = CreateObject("Scripting.FileSystemObject")
            With fso.OpenTextFile(FileName)
                s = .ReadAll
                .Close
            End With
            
            Set re = CreateObject("VBScript.RegExp")
            With re
                .Global = True
                .Pattern = "<(!.+;)(?:\n|.)+?END"
                Set Match = .Execute(s)
                If Not Match Is Nothing Then
                    [B][COLOR=#ff0000]s = .Replace(s, "")
                    With fso.CreateTextFile(FileName & "_error.log")
                        .Write s
                        .Close
                    End With[/COLOR][/B]
                    
                    ReDim Arr(1 To 1)
                    .Pattern = "\s\d{2}(\d{7})\s"
                    For i = 0 To Match.count - 1
                        Set oMatches = .Execute(Match(i))
                        If Not oMatches Is Nothing Then
                            ReDim Preserve Arr(1 To count + 2 + oMatches.count)
                            count = count + 1
                            Arr(count) = Match(i).SubMatches(0)
                            For r = 0 To oMatches.count - 1
                                count = count + 1
                                Arr(count) = "n=" & oMatches(r).SubMatches(0) & ";"
                            Next r
                            count = count + 1
                        End If
                        Set oMatches = Nothing
                    Next
                End If
                Set Match = Nothing
            End With
            Set re = Nothing
            [B][COLOR=#0000ff]Set fso = Nothing[/COLOR][/B]
            
            If count Then
                ReDim result(1 To count - 1, 1 To 1)
                For r = 1 To count - 1
                    result(r, 1) = Arr(r)
                Next r
                [A1].Resize(UBound(result)) = result
            End If
        End If
End Sub

Chỗ đỏ đỏ là tôi thêm vào để ghi dữ liệu lỗi. Chỗ xanh xanh là chuyển từ trên xuống

code trả về:

Chỉ sai 2/90 trạm, dữ liệu của em bị lỗi ở 2 trạm này.
Một lần nữa em chân thành cảm ơn thầy. Chúc thầy mạnh khoẻ.

Chuyện đếm là chuyện đơn giản. Cái khó là đếm theo đặc điểm gì.
Tôi đã giả thiết là mỗi thuê bao bắt đầu bằng <suscp:snb= và kết thúc bằng END. Sau đó tôi nghĩ: thế nếu thuê bao nào đó thiếu END (lỗi như lần trước) thì sao? Rồi tôi từ bỏ "đặc điểm" này và đếm theo đặc điểm: mỗi thuê bao là số có 9 chữ số. Kết quả sai ở 2 trạm tức là trong dữ liệu 2 trạm đó có "thêm" những số có 9 chữ số mà không phải là thuê bao.

Vậy tôi cho bạn phiên bản 2: lần này là đếm theo đặc điểm là "mỗi thuê bao bắt đầu bằng <suscp:snb= và kết thúc bằng END". Kết quả bây giờ thì đúng với cái bạn liệt kê.

Nhưng tôi nhắc lại: dù đếm theo cách nào thì cũng có nguy hiểm rình rập. Theo số có 9 chữ số thì khi có thêm số có 9 chữ số mà không là thuê bao thì toi. Mà đếm theo "mỗi thuê bao bắt đầu bằng <suscp:snb= và kết thúc bằng END" mà thiếu END thì cũng toi.

code phiên bản 2 cho bài #19
Mã:
Sub DoSomething()
    Dim fso As Object, re As Object, Match As Object, oMatches As Object
    Dim FileName, Arr(), i As Long, s As String
        
        FileName = Application.GetOpenFilename()
        If FileName <> "False" Then
            Set fso = CreateObject("Scripting.FileSystemObject")
            With fso.OpenTextFile(FileName)
                s = .ReadAll
                .Close
            End With
            Set fso = Nothing
            Set re = CreateObject("VBScript.RegExp")
            With re
                .Global = True
                .Pattern = "<(!.+;)(?:\n|.)+?(?=(?:<!.+;|$))"
                Set Match = .Execute(s)
                If Not Match Is Nothing Then
                    ReDim Arr(1 To Match.count, 1 To 4)
                    
                    For i = 0 To Match.count - 1
                        Arr(i + 1, 1) = Match(i).SubMatches(0)
                        
                        .Pattern = "[COLOR=#ff0000]<suscp:snb=(?:\n|.)+?END[/COLOR]"
                        Set oMatches = .Execute(Match(i))
                        If Not oMatches Is Nothing Then Arr(i + 1, 2) = oMatches.count
                        
                        .Pattern = "\sTBO-1\s"
                        Set oMatches = .Execute(Match(i))
                        If Not oMatches Is Nothing Then Arr(i + 1, 3) = oMatches.count
                        
                        .Pattern = "\sTBO-2\s"
                        Set oMatches = .Execute(Match(i))
                        If Not oMatches Is Nothing Then Arr(i + 1, 4) = oMatches.count
                    Next
                    [A2].Resize(UBound(Arr), 4) = Arr
                End If
            End With
        End If
End Sub

Chỗ đỏ đỏ là sửa lại
 
Code phiên bản 2 của thầy thực sự rất tối ưu, kiểm soát tình hình dữ liệu tốt hơn, liệt kê được thiết bị nào chưa khai báo thuê bao, và những thuê bao chưa khai báo trạm ở <file>_error.log
Code phiên bản 2 cho bài #19 thống kê theo vòng lặp
tối ưu hơn "đặc điểm" lấy 9 chữ số. Vì mỗi lệnh tổng đài kết thúc đều có END, nên sẽ không có trường hợp thiếu END. Tất nhiên cũng không loại trừ nguy hiểm do người dùng vô tình xoá mất 1 END nào đó ở trong log.
Những lập luận rất chuyên nghiệp thầy đưa ra đã giúp cho em mở mang được phần nào kiến thức của mình.
. Nhưng đã code thì để cho nó làm max việc còn mình có max thời gian uống cà phê.
Vậy là em có thời gian đi uống cà phê rồi thầy ạ. Một lần nữa xin cảm ơn thầy.
 
Web KT

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

Back
Top Bottom