【问题标题】:Perform file encoding conversion with Rebol 3使用 Rebol 3 执行文件编码转换
【发布时间】:2014-03-10 02:03:53
【问题描述】:

我想使用 Rebol 3 读取 Latin1 格式的文件并将其转换为 UTF-8。是否有我可以使用的内置函数或一些外部库?我在哪里可以找到它?

【问题讨论】:

  • 旁注:我认为在这个时代,将您的输入文件本身切换为 UTF-8 是最好的主意......如果您的情况允许的话。这很容易使用下面的@earl 方法完成,然后将其写回。例如,write %myfile.utf8 (latin1-to-utf8 read %myfile.latin1) 然后你不必关心转换的速度有多慢,因为你是只做一次...
  • 完全同意你的看法。我所有的文件都是utf8。 :-) 但其他一些不是 :-(

标签: character-encoding rebol file-conversion rebol3


【解决方案1】:

Rebol 有一个 invalid-utf? 函数,该函数可以搜索二进制值以查找不属于有效 UTF-8 序列的字节。我们可以循环直到找到并替换所有这些,然后将我们的二进制值转换为字符串:

latin1-to-utf8: function [binary [binary!]][
    mark: :binary
    while [mark: invalid-utf? mark][
        change/part mark to char! mark/1 1
    ]
    to string! binary
]

此函数修改原始二进制文件。我们可以创建一个新字符串来保持二进制值不变:

latin1-to-utf8: function [binary [binary!]][
    mark: :binary
    to string! rejoin collect [
        while [mark: invalid-utf? binary][
            keep copy/part binary mark  ; keeps the portion up to the bad byte
            keep to char! mark/1        ; converts the bad byte to good bytes
            binary: next mark           ; set the series beyond the bad byte
        ]
        keep binary                     ; keep whatever is remaining
    ]
]

奖励:这是上面的 Rebmu 的一个小版本 - rebmu/args snippet #{DECAFBAD} 其中snippet 是:

; modifying
IUgetLOAD"invalid-utf?"MaWT[MiuM][MisMtcTKm]tsA

; copying
IUgetLOAD"invalid-utf?"MaTSrjCT[wt[MiuA][kp copy/partAmKPtcFm AnxM]kpA]

【讨论】:

  • 与其他提议的变体不同,此版本不返回 binary!,而是返回 string!(因此从技术上讲,它仅将拉丁语 1 解码为 Unicode,但不会再次将其编码为 UTF- 8)。因此,对于真正的 Latin-1 到 UTF-8 转码,只需省略 to string! 调用。
  • 比其他人快得多:修改版本 9x,复制版本 13x。伟大的@rgchris!
  • @earl 猜我没注意。正如您所说,在这两种情况下,删除to string! 并返回二进制文件都很容易。并感谢您的链接:)
【解决方案2】:

这里有一个版本应该更快一些,至少使用更少的内存。

latin1-to-utf8: func [
    "Transcodes a Latin-1 encoded string to UTF-8"
    bin [binary!] "Bytes of Latin-1 data"
] [
    to binary! head collect/into [
        foreach b bin [
            keep to char! b
        ]
    ] make string! length? bin
]

它利用了与相应 Unicode 代码点具有相同数值的 Latin-1 字符。如果您想从另一个字符集转换而并非如此,您可以对 b 进行计算以重新映射字符。

由于各种原因,它使用更少的内存并且速度更快:

  • 通常,collect 创建一个块。我们使用collect/into 并将其作为目标传递给它。字符串比整数块或字符块使用更少的内存。
  • 我们将字符串预分配到输入数据的长度,这样可以节省重新分配。
  • 我们让 Rebol 的本机代码转换字符,而不是自己进行数学运算。
  • 循环中的代码更少,因此它应该运行得更快。

这个方法仍然是一次性将文件全部加载到内存中,仍然会生成一个中间值来存储结果,但至少中间值更小。也许这会让你处理更大的文件。

如果您需要它为 UTF-8 的原因是您需要在 Rebol 中将文件作为字符串处理,只需跳过 to binary! 并按原样返回字符串。或者您可以只处理二进制源数据,只需在每个字节上使用to char! 转换二进制中的字节即可。

【讨论】:

  • 非常整洁,+1!特别是。用于记住 Latin1 到代码点的对应关系。也是(上限)整数效用的一个很好的例子!值作为二进制的元素!。
  • 发现 'append 比 collect/into 快 2 倍。看我的回答。
【解决方案3】:

目前没有内置任何功能,抱歉。这是我不久前编写并与 Rebol 3 一起使用的 Latin-1 到 UTF-8 转换的简单实现:

latin1-to-utf8: func [
    "Transcodes a Latin-1 encoded string to UTF-8"
    bin [binary!] "Bytes of Latin-1 data"
] [
    to-binary collect [foreach b bin [keep to-char b]]
] 

注意:此代码已针对易读性进行了优化,而绝不是为了提高性能。 (从性能的角度来看,这完全是愚蠢的。您已被警告过。)

更新: 合并了 @BrianH 的简洁的“Latin-1 字节值对应于 Unicode 代码点”优化,这使得上面的代码崩溃为单行(同时稍微不那么愚蠢)。仍然。有关内存使用的更优化版本,请参阅@BrianH 的好答案。

【讨论】:

  • @giuliolunati 请注意,to-binary data 是我认为更好的表达方式的一个小包装:to binary!数据。通过将 TO 直接用于数据类型,您意识到您可以将类似 的内容写入(任一条件 [字符串!] [二进制!])数据。为什么有些人认为他们一直使用的感叹号来指定类型太刺耳并且需要包装器让我难以理解,但有些人确实如此。但是,如果您对某些东西是否是内置的感到好奇,您可以使用 source,例如source to-binary 并看到它调用 TO 并且不是 native!action!
【解决方案4】:
latin1-to-utf8: func [
    "Transcodes bin as a Latin-1 encoded string to UTF-8"
    bin [binary!] "Bytes of Latin-1 data"
    /local t
] [
    t: make string! length? bin
    foreach b bin [append t to char! b ]
    t
]

【讨论】:

  • 与其他提议的变体不同,此版本不返回 binary!,而是返回 string!(因此从技术上讲,它仅将拉丁语 1 解码为内存中的 Unicode,但不会再次对其进行编码到 UTF-8)。要解决此问题,您必须将最后一行更改为 to binary! t
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-20
  • 1970-01-01
  • 2012-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多