2017-10-19 12 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,则将两个字符串放在一起,然后完成。 –