Chia sẻ: Hàm lấy dữ liệu từ file text vào Excel và ghi từ Excel sang file text.

Liên hệ QC

Maika8008

Thành viên gạo cội
Tham gia
12/6/20
Bài viết
4,763
Được thích
5,718
Donate (Momo)
Donate
Giới tính
Nam
Thấy nhiều người có nhu cầu lấy dữ liệu từ file dạng text như .txt, .xml,... sang Excel để xử lý tổng hợp, báo cáo, đồng thời cũng xuất dữ liệu từ Excel lưu sang file dạng text, cho nên tôi viết 2 hàm để làm điều ấy.

1. Hàm ấy dữ liệu từ file text vào Excel: Kết quả trả về của hàm là 1 cửa sổ Excel mới chứa dữ liệu lấy được. Có thể gọi hàm từ code VBA để lưu vào mảng hoặc ghi ra sheet, hoặc gõ trực tiếp trên cell của trang tính. Khi gọi hàm trực tiếp trên trang tính thì tại cell chứa công thức sẽ chỉ chuỗi "Xong roi! và sau đó chuyển sang cửa sổ kết quả.
Rich (BB code):
Function GetText_FromFile(ByVal sFullName As String, Optional ByVal OptSeparator = vbTab)
'sFullName - ten file voi duong dan day du
'OptSeparator - Tuy chon dau phan cach du lieu giua cac truong (field) trong 1 dong (record). Mac dinh la vbTab

Dim st As Object, ExApp As Object, ExBook As Object
Dim sMyFile$, sText$
Dim aTmp, aLine, aRsl
Dim iLine&, i&, j&, iCol&

    On Error GoTo Loi
    sMyFile = sFullName
    Set st = CreateObject("ADODB.Stream")
    
    With st
        .Charset = "utf-8"
        .Open
        .LoadFromFile (sMyFile)
        sText = .ReadText(-1)
        
        If InStr(1, sText, Chr(10)) Then
            aTmp = Split(sText, Chr(10))
            iLine = UBound(aTmp)
            ReDim aRsl(1 To iLine, 1 To 1000)
            For i = 0 To iLine - 1
                aLine = Split(aTmp(i), OptSeparator)
                If UBound(aLine) > iCol Then iCol = UBound(aLine)
                For j = 0 To UBound(aLine)
                    aRsl(i + 1, j + 1) = aLine(j)
                Next
            Next i
            If UBound(aRsl) > 0 And iCol = 0 Then iCol = 1
            ReDim Preserve aRsl(1 To iLine, 1 To iCol)
            Set ExApp = CreateObject("Excel.Application")
            Set ExBook = ExApp.Workbooks.Add
            ExApp.Visible = True
            ExBook.Sheets(1).Range("A1").Resize(UBound(aRsl), UBound(aRsl, 2)).Value = aRsl
            GetText_FromFile = "Xong roi!"
            ExBook.Activate
        Else
            GetText_FromFile = sText
        End If
        .Close
    End With
    
    Set st = Nothing
    Exit Function
Loi:
    Set st = Nothing
    MsgBox "Da co loi." & vbNewLine & "Ma loi: " & Err.Number & vbNewLine & Err.Description
End Function

2. Hàm ghi từ Excel sang file text: Kết quả trả về của hàm file text theo định dạng do người chỉ định trong đối số của hàm và lưu cùng đường dẫn với file chứa hàm. Có thể gọi hàm từ code VBA hoặc gõ trực tiếp trên cell của trang tính. Khi gọi hàm trực tiếp trên trang tính thì tại cell chứa công thức sẽ trả về đường dẫn file kết quả.
Mã:
Function WriteText_ToExistsFile(ByVal MyRange As Range, ByVal SFile As String, _
                                Optional ByVal dExt As String = ".txt", _
                                Optional ByVal OptJoin As Boolean = True)
'MyRange - Vung du lieu can ghi vao file text
'sFile - File nguon .txt (hoac co the laf 1 file dang text bat ky: sql, bat, css,...)
'dExt - Phan mo rong cua file muon luu lai: Mac dinh la ".txt", giong file nguon. Muon luu dang khac thi ghi ro ".sql", ".bat", ...
'OptJoin - Tuy chon noi du lieu file nguon voi du lieu moi (True: noi du lieu cu, False: ghi moi, xoa du lieu cu)

Dim st As Object, aData
Dim i&, j&, iDot&, iSCells
Dim sMyFile$, sFileSaveAs$, sWrite$

    On Error GoTo Loi
    sMyFile = ThisWorkbook.Path & "\" & SFile  'File nguon
    iDot = InStr(1, dExt, ".")
    If iDot = 0 Then
        sFileSaveAs = ThisWorkbook.Path & "\" & Left(SFile, InStr(1, SFile, ".")) & dExt
    Else
        sFileSaveAs = ThisWorkbook.Path & "\" & Left(SFile, InStr(1, SFile, ".") - 1) & dExt
    End If
    aData = MyRange.Value           'Du lieu muon ghi vao file text
    iSCells = MyRange.Cells.Count
    
    Set st = CreateObject("ADODB.Stream")
    With st
        .Type = 2
        .Charset = "utf-8"
        .Open
        If OptJoin = True Then
            .LoadFromFile (sMyFile)   'Mo file
            .ReadText -1    'Doc file
        End If
        If iSCells = 1 Then
            .WriteText aData & Chr(10)
        Else
            For i = 1 To UBound(aData, 1)
                For j = 1 To UBound(aData, 2)
                    sWrite = sWrite & IIf(aData(i, j) = "", "NULL", aData(i, j)) & vbTab
                Next
                .WriteText sWrite & Chr(10): sWrite = ""
            Next
        End If
        On Error Resume Next
        .SaveToFile sFileSaveAs, 2 'Luu file
        .Close
    End With
    Set st = Nothing
    WriteText_ToExistsFile = "Da luu file tai " & sFileSaveAs
    Exit Function
Loi:
    Set st = Nothing
    MsgBox "Da co loi." & vbNewLine & "Ma loi: " & Err.Number & vbNewLine & Err.Description

End Function

Tất nhiên khi viết tôi chưa lường hết các nhu cầu người dùng về cách lấy text và cũng không thể tránh khỏi lỗi chạy. Nếu sử dụng có lỗi hoặc nhu cầu gì thêm, các bạn báo lại cho tôi biết để chỉnh sửa.
 
Chia sẻ với các bạn file Text với các dung lượng khác nhau từ vài trăm Mb tới 2G, từ 800.000 dòng (12 cột) - 16 triệu dòng. Có nguồn dữ liệu để test các công cụ nhé.

Link: Big Text Files
 
Upvote 0
Text có rất nhiều encoding.
Tập tin text đâu phải lúc nào encoding cũng là utf-8 đâu anh.

Ví dụ

View attachment 278496

Cần nhận biết được encoding là gì mới lấy được dữ liệu chuẩn.

Anh mở trên notepad++ với Character set là Chinese simplified mà thấy nó không giống với hình em gửi nhỉ? nó encode theo mã gì vậy befaint?

Screen Shot 2022-07-11 at 12.24.23.png

Chạy thử cái code python Chardetect, nó cũng không ra cái mã gì luôn (None), khó thật.

Screen Shot 2022-07-11 at 13.39.58.png


Oh.. dùng cchardet thì nó ra giống. :D. Và giờ mới biết nó là tiếng Nhật Bổn, hèn chi chọn mã Chinese trong Notepad++ nó không giống.

Screen Shot 2022-07-11 at 13.57.55.png
 
Lần chỉnh sửa cuối:
Upvote 0
(つまりは Shift_JIS) .... hình như là nó
 
Upvote 0
Em chào AnhMaika8008, và các Anh/Chị trong diễn đàn, bài này đúng cái em đang cần lấy dữ liệu từ file text.

Em chưa rõ phân biệt OptSeparator - tùy chọn dấu phân cách giữa các trường trong 1 dòng.
Anh có thể giải thích rõ hơn giúp em được không ạ.
Em có sử dụng code của Anh nhưng không biết thay thế OptSeparator trong file của mình nên kết quả chưa ra được như mong muốn.

Em gửi file tẽt (do file dữ liệu của cty có đuôi rtf, nên em cắt bớt, zip lại) và file excel em mong muốn kết quả.
Mong các Anh/Chị giúp em ạ.
Em cảm ơn các Anh/Chị
 

File đính kèm

  • Text 2 Excel.xlsx
    19.7 KB · Đọc: 5
  • CRB.zip
    5.6 KB · Đọc: 8
Upvote 0
Em chào AnhMaika8008, và các Anh/Chị trong diễn đàn, bài này đúng cái em đang cần lấy dữ liệu từ file text.

Em chưa rõ phân biệt OptSeparator - tùy chọn dấu phân cách giữa các trường trong 1 dòng.
Anh có thể giải thích rõ hơn giúp em được không ạ.
Em có sử dụng code của Anh nhưng không biết thay thế OptSeparator trong file của mình nên kết quả chưa ra được như mong muốn.

Em gửi file tẽt (do file dữ liệu của cty có đuôi rtf, nên em cắt bớt, zip lại) và file excel em mong muốn kết quả.
Mong các Anh/Chị giúp em ạ.
Em cảm ơn các Anh/Chị
Thường thì file text cần lấy dữ liệu có cấu trúc khá đơn giản: hoặc là mỗi dòng chỉ 1 thông tin, hoặc là các dòng có số lượng thông tin cố định phân cách với nhau bằng 1 ký tự quy ước như TAB hoặc ;

File của bạn có cấu trúc phức tạp gồm rất nhiều bảng và trên (hoặc dưới) mỗi bảng có các thuyết minh nên muốn lấy hoàn chỉnh thì không dùng cái hàm đơn giản như trên mà phải viết 1 sub phức tạp.

Còn nếu vẫn muốn lấy dữ liệu theo kiểu có gì lấy nấy thì bạn không cần quan tâm đến OptSeparator mà chỉ cần gõ trên cell như sau:
=GetText_FromFile("D:\TestGPE" & "\" & "CRB.RTF")

Nhớ thay đường dẫn
 
Lần chỉnh sửa cuối:
Upvote 0
Thường thì file text cần lấy dữ liệu có cấu trúc khá đơn giản: hoặc là mỗi dòng chỉ 1 thông tin, hoặc là các dòng có số lượng thông tin cố định phân cách với nhau bằng 1 ký tự quy ước như TAB hoặc ;

File của bạn có cấu trúc phức tạp gồm rất nhiều bảng và trên (hoặc dưới) mỗi bảng có các thuyết minh nên muốn lấy hoàn chỉnh thì không dùng cái hàm đơn giản như trên mà phải viết 1 sub phức tạp.

Còn nếu vẫn muốn lấy dữ liệu theo kiểu có gì lấy nấy thì bạn không cần quan tâm đến OptSeparator mà chỉ cần gõ trên cell như sau:
=GetText_FromFile("D:\TestGPE" & "\" & "CRB.RTF")

Nhớ thay đường dẫn
dạ, em có dùng công thức anh nói ở trên nhưng nó chỉ như copy file text sang excel thôi, chưa tách được cột như mong muốn nên em mới đang nhờ anh giải thích thêm phần OptSeparator- dấu phân cách giữa các trường trong 1 dòng.
Thì cái OptSeparator này xem như thế nào để phân biệt lúc nào là vbCrLf, hay vbTab...
Em có tìm hiểu trong vba có kiểu như sau, nhưng không biết cách nhìn trong file text như nào để phân biệt ạ.
Mong Anh có thể nói rõ hơn được không ạ
  • vbCrLf = chr(13) + chr(10) : xuống dòng và về đầu dòng
  • vbCr = chr(13) : xuống dòng
  • vbLf = chr(10) : về đầu dòng
  • vbNewLine = chr(13) + chr(10) : Ký tự dòng mới
  • vbTab = chr(9) : ký tự Tab
  • vbBack = chr(8) : ký tự BackSpace
 
Upvote 0
dạ, em có dùng công thức anh nói ở trên nhưng nó chỉ như copy file text sang excel thôi, chưa tách được cột như mong muốn nên em mới đang nhờ anh giải thích thêm phần OptSeparator- dấu phân cách giữa các trường trong 1 dòng.
Thì cái OptSeparator này xem như thế nào để phân biệt lúc nào là vbCrLf, hay vbTab...
Em có tìm hiểu trong vba có kiểu như sau, nhưng không biết cách nhìn trong file text như nào để phân biệt ạ.
Mong Anh có thể nói rõ hơn được không ạ
  • vbCrLf = chr(13) + chr(10) : xuống dòng và về đầu dòng
  • vbCr = chr(13) : xuống dòng
  • vbLf = chr(10) : về đầu dòng
  • vbNewLine = chr(13) + chr(10) : Ký tự dòng mới
  • vbTab = chr(9) : ký tự Tab
  • vbBack = chr(8) : ký tự BackSpace
Cái bảng dữ liệu trong file text của bạn không có quy cách khi phân cách giữa các cột chi hết. Theo tôi thấy khi xem trong Notepad ++ thì có rất nhiều ký tự (không đồng đều nhau về số lượng) phân cách các cột. Không biết đó là ký tự gì mà thể hiện ra là dấu chấm. Do vậy, tôi nghĩ dù bạn có biết ký tự đó là chr(xx) gì thì cũng không lấy ra được cái bảng đúng nghĩa với cái hàm của tôi mà sẽ ra 1 mớ chữ, số lộn xộn.

P/S: trong notepad++, bạn cho hiện ký tự đặc biệt mới có thể thấy như tôi mô tả, còn không thì sẽ thấy toàn khoảng trắng.
 
Upvote 0
Cái bảng dữ liệu trong file text của bạn không có quy cách khi phân cách giữa các cột chi hết. Theo tôi thấy khi xem trong Notepad ++ thì có rất nhiều ký tự (không đồng đều nhau về số lượng) phân cách các cột. Không biết đó là ký tự gì mà thể hiện ra là dấu chấm. Do vậy, tôi nghĩ dù bạn có biết ký tự đó là chr(xx) gì thì cũng không lấy ra được cái bảng đúng nghĩa với cái hàm của tôi mà sẽ ra 1 mớ chữ, số lộn xộn.

P/S: trong notepad++, bạn cho hiện ký tự đặc biệt mới có thể thấy như tôi mô tả, còn không thì sẽ thấy toàn khoảng trắng.
Vâng, để em thử cài notepad++ xem ạ, máy em mới có bản notepad nên không nhìn thấy ạ
 
Upvote 0
Split + Chuyển mảng trên VBA với số dòng 1048570 là Out of Memory :p
Hãy tìm kiếm 1 thuật toán mới thay thế nó ... tại sao Không ???!!!!!!!!!
 
Upvote 0
Split + Chuyển mảng trên VBA với số dòng 1048570 là Out of Memory :p
Hãy tìm kiếm 1 thuật toán mới thay thế nó ... tại sao Không ???!!!!!!!!!
Tạm thời, nếu thay thế split thì tôi chỉ biết dùng instr duyệt dấu phân cách optSeparator đến hết dòng thôi à. Mấy hôm nay bận nên chưa thử cái file 1 triệu dòng kia.
 
Upvote 0
Đối với tôi việc đọc file Text (CSV) này phải vạch rõ mục đích để làm gì và dùng công cụ phù hợp với nó thì hay hơn.
Giống như việc muốn đi chơi, ra Vũng Tàu cho nhanh mà trang bị một chiếc xe đạp cực xịn để đi thì ra tới nơi cũng đuối.
Cái hàm của bạn Maika8008 dùng ADODB Stream thì dùng thích hợp cho việc xử lý các file Text với số dòng không lớn (theo tôi khoảng <50k dòng). Khi số dòng dữ liệu càng lớn thì công việc nó nghiêng về chuyên xử lý dữ liệu thì tội gì phải bám vô VBA, Excel, khai thác đến mức tới hạn của nó làm gì, nó chạy ì ạch, lãng phí thời gian.
Khi đã đọc dữ liệu thì phải tính tới chuyện: nơi lưu dữ liệu đã đọc đó, xử lý dữ liệu rồi xuất ngược lại...Excel không phải là nơi để ghi dữ liệu lớn xuống. Access có thể lưu dữ liệu tốt nhưng cũng hỗ trợ tối đa 2G. Dùng mySQL, SQL Server với Bulk insert thì cũng xử lý một cách nhẹ nhàng.
Không cần phải nói tới Python, tôi thấy có các công cụ người ta viết sẵn chuyên xử lý file CSV dung lượng lớn (100 triệu dòng) như CSV Explorer, klogg, notepad++ v.v..Nó đọc rất nhanh và không phải thở hì hục.
Muốn dùng công cụ có sẵn thì cứ Power Query, Access mà đọc + xử lý dữ liệu lớn.
Tôi có test thử việc dùng code VBA trong Excel, Access xem nó đọc ghi 800k dòng dữ liệu như thế nào thì thấy:
- Dùng Access là nhanh hơn cả - 6 giây, kế đó là Power Query - 8 giây.
- Dùng ADO mất 22s.
- Dùng hàm ADO Stream ở trên thì treo máy luôn. Báo "Not enough storage to perform..."
- Dùng Free file để đọc ghi vào Buffer thì mất khá là nhiều thời gian: 52s

Một điểm đặc biệt của Access là chỉ cần 1 dòng lệnh để chạy tác vụ này :) .

PHP:
DoCmd.TransferText acImportDelim, , "tblCSVImported", Me.txtCSVFilePath, True


Video test: Máy tôi Core i5 1.4 GHz Quad, 8G RAM
(Chỉ tham khảo chơi thôi nhé, còn nhiều yếu tố chưa tính đến như: tính đúng đắn của dữ liệu, cấu hình máy...)

 
Lần chỉnh sửa cuối:
Upvote 0
1657799926006.png

Rất quan trọng ở đây là ta tự viết lấy nó ra hết mấy giây ... còn tây nó viết cho mình thì mát lạnh như kem vậy còn ta chỉ việc mút thì nó sẻ khác 1 chút thôi
 
Upvote 0
Upvote 0
rảnh tôi sẻ xuất 1 hàm API load file *.CSV như sau cũng chỉ khoãng 6 to 9 s thôi cho 800.000 dòng
xong muốn lên Sheet hay Listbox tùy chỉnh

1657854237932.png
 
Upvote 0
rảnh tôi sẻ xuất 1 hàm API load file *.CSV như sau cũng chỉ khoãng 6 to 9 s thôi cho 800.000 dòng
xong muốn lên Sheet hay Listbox tùy chỉnh
6 - 9s trên máy cấu hình như thế nào mới đánh giá sơ bộ được chứ. Trên Access chạy cái rẹt với 1 dòng code, chừng 5s là vô Table rồi (cấu hình máy tôi ở hình trên).
 
Upvote 0
Web KT

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

Back
Top Bottom