2017-10-19 32 views
1

推薦的技術,用於讀取UTF-8的文件是:讀UTF-8編碼的文件,其中包括4字節編碼到Excel

Dim FileStream As Stream 
    Dim FileBodyADO As String 

    Set FileStream = CreateObject("ADODB.Stream") 

    With FileStream 
    .Charset = "utf-8" 
    .Open 
    .LoadFromFile ("C:\DataArea\Resources\VBA Outlook\Tutorial\examples.json") 

    FileBodyADO = .ReadText() 

    .Close 
    End With 

    Set FileStream = Nothing 

然而,如果你試圖讀取「examples.json」,這是部分的存檔的SO文檔,陳述FileBodyADO = .ReadText()永遠不會結束。

的UTF-8文件是一個字節定向文件(不同的是,例如,UTF-16),字符,其代碼是在範圍爲0〜& H7F進行不變,並與編碼以多字節以上& H7F碼字符序列:

-Code (Hex)- ---------------Encoding--------------- 
    Start End Byte 1 Byte 2 Byte 3 Byte 4 
     0  7F 0xxxxxxx 
    80 7FF 110xxxxx 10xxxxxx 
    800 FFFF 1110xxxx 10xxxxxx 10xxxxxx 
    10000 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 

「examples.json」包含:

92,601,220 1-byte encodings 
    8,848 2-byte encodings 
    20,122 3-byte encodings and 
     166 4-byte encodings. 

1字節的編碼是ASCII字符。例如,2字節編碼是§(HA7)和I(H131)。例如,3字節編碼是√(H221A)和年(H5E74)。例如,4字節編碼是「Sparkling Heart」(H1F496)和「笑臉笑臉」(H1F601)。

我找不到任何建議Office產品可以處理U + FFFF以上代碼的字符。我懷疑ReadText在遇到4字節編碼時會以無限循環結束。

我有我自己的VBA例程來解碼UTF-8文件。它們在第一次遇到時也無法處理4字節編碼。自從我增強/糾正了我的例程以接受4字節編碼並將它們解碼爲數字字符實體。例如,「笑臉笑臉」(H1F601)作爲HF0 9F 98 81在文件中攜帶,我的例程解碼爲😁。如果此數字字符實體放置在html文件中,則Microsoft Edge將顯示正確的表情符號。我瞭解谷歌瀏覽器和大多數(所有?)現代瀏覽器也可以處理這種數字字符實體。你能看到什麼:😁?由於包含這些字符的文本是html,因此我的解決方案足以滿足當前的要求。

我會在幾天內發佈我的例程作爲答案,除非先發布更好的答案。人們是否同意ADODB的ReadText被4字節編碼擊敗? Office產品,特別是Excel可以處理Plane 1 Unicode字符(H10000至H1FFFF)嗎?我有使用數字字符的替代方法嗎?

更多的背景

我已經接受了來自湯姆·布洛杰特的答案,因爲它不回答我的問題。但是,這不是我希望的答案。

幾年前,我收到了包含UTF-16,UTF-8,ASCII和ISO-8859-1的不同格式的文件。這些文件的作者從不同的應用程序中提取數據,但我發現各種格式都是意想不到的;根據我的經驗,現在大多數應用程序都使用UTF-8。我的供應商都不知道他們的源應用程序創建了什麼格式,或者如何將輸出格式更改爲UTF-8或其他一致。

「傳統VBA」將讀取或寫入ASCII或Unicode(Microsoft表示OCS-2)文件。顯然,OCS-2與UTF-16「幾乎相同」。對我而言,「幾乎相同」意味着不同,但我無法解釋它們的不同之處。 ADODB是一個可以接受其他格式的VBA庫,但所有文檔都意味着您必須知道該格式是什麼。像NotePad ++這樣的工具將打開任何文本文件並告訴你它的格式。我無法找到與VBA相似的東西。

我決定寫我自己的代碼,將每個文件讀入一個字節數組並確定格式。識別格式並不是很少能夠識別和轉換爲VBA字符串,這就是我所做的。這些文件不是特別大,所以讀取和轉換時間少於0.01秒,這足以滿足我的需求。

當我需要閱讀「examples.json」時,我自然使用了我的例程。我現在知道「examples.json」包含166個4字節的編碼,而且我的例程沒有正確處理它們。我修復了我的例程中的錯誤,並對結果感到滿意,除了用最新版本處理92Mb文件需要34秒。我嘗試了ADODB,看看它有多快,但它永遠不會終止。在我問這個問題之前,這是我得到的。

我讀過ADODB不是很有效,你應該一次讀一小塊。然而,在我嘗試Tom Blodget的答案之前,我並沒有把「低效率」等同於「不終止」。按照建議通過優化ADODB的使用,它現在終止。研究輸出增加了我對UTF-8編碼的理解,所以這是一個有用的練習。然而,在大約40秒鐘,ADODB甚至比我的VBA例程慢。

在我的筆記本電腦,下面的代碼讀取整個92MB文件到一個字節數組中約0.1秒時:

FileNum = FreeFile 
    Open PathFileName For Binary Access Read As FileNum 
    ReDim FileBodyByte(1 To LOF(FileNum)) 
    Get FileNum, , FileBodyByte 
    Close FileNum 
字節數組

一旦,轉換爲字符串是完全受處理器限制。爲什麼ADODB需要在128K塊中讀取塊?如果塊在編碼中間結束會發生什麼?爲什麼需要這麼長時間?我已將處理器綁定的VBA例程轉換爲VB.Net,並將持續時間縮短了1,000倍。我不覺得舒服,使用ADODB是我發佈給客戶的例程。

微軟似乎癡迷於向前兼容性。我15年前寫的VBA代碼仍然有效。微軟並沒有增強老套路。如果要提供新的功能,它會引入新的庫。 ADODB是舊的。我希望有新的更好的東西,而不是解決方法。

+0

否,ADODB.Stream處理UTF-8編碼的字符與4個編碼單元就好了。這個問題似乎是別的,也許是文件大小。 –

回答

0

ADODB.Stream。 ReadText文檔解釋了效率問題,並建議一次讀取更少的字節以緩解它。

所以,

Dim FileStream As Stream: Set FileStream = CreateObject("ADODB.Stream") 

    With FileStream 
    .Charset = "utf-8" 
    .Open 
    .LoadFromFile "C:\Users\Tom\Downloads\examples.json" 

    Dim FileBodyADO As String: FileBodyADO = "" 
    While Not .EOS 
     FileBodyADO = FileBodyADO & .ReadText(128 * 1024&) 
    Wend 
    Debug.Print Len(FileBodyADO) 
    Debug.Assert Len(FileBodyADO) = 92630322 
    .Close 
    End With 

    Set FileStream = Nothing 

由於.NET獨立告訴你,examples.json包含文本UTF-16 92630322個代碼單元編碼。 (VBA/VB4 +使用UTF-16字符串[1990年代初以來作爲這樣做的許多其他語言。)

File.ReadAllText(@"C:\Users\Tom\Downloads\examples.json").Length == 92630322 
+0

我認爲效率問題,但拒絕他們作爲一個原因。在放棄它之前,我離開了'ADODB.ReadText'一小時。我的VBA例程需要28秒來解碼這個92Mb文件。我期望'ADODB.ReadText'更快。 「ADODB.ReadText」會將4字節編碼解碼爲什麼?我無法找到任何說明/建議/暗示VBA字符串或Excel字符串可能包含U + 10000以上代碼字符的文檔。我發現的文檔建議如果在Excel中想要,可以將表情符號作爲圖像使用。如果你知道不同,請鏈接到相應的文檔。 –

+0

我會試驗你的代碼。如果它適用於我,我會嘗試發現4字節編碼已解碼爲什麼。我的代碼點數高於你得到的值,雖然這可能是我的代碼中的一個錯誤來計算它們。話雖如此,我的計數乘以編碼長度給出了正確的文件長度。 –

+0

微軟傾向於調用Unicode字符集「Unicode」的USC-2編碼,然後將其應用於其替換UTF-16。 USC-2和UTF-16之間的區別並不是典型的語言或圖書館問題。 [ChrW](https://msdn.microsoft.com/en-us/vba/language-reference-vba/articles/chr-function)文檔幾乎無法解釋,術語「字符」不明確,但ChrW僅處理UTF-16編碼單元;如果代碼點> U + FFFF,則將兩個字符串放在一起,然後完成。 –