Character encoding 使用Rebol 3执行文件编码转换

Character encoding 使用Rebol 3执行文件编码转换,character-encoding,rebol,file-conversion,rebol3,Character Encoding,Rebol,File Conversion,Rebol3,我想使用REBOL3读取拉丁文1中的文件并将其转换为UTF-8。是否有我可以使用的内置函数,或一些外部库?我在哪里可以找到它?现在没有内置的,对不起。下面是一个简单的拉丁语-1到UTF-8转换的实现,这是我不久前编写并与Rebol 3一起使用的: latin1-to-utf8: func [ "Transcodes a Latin-1 encoded string to UTF-8" bin [binary!] "Bytes of Latin-1 data" ] [ to

我想使用REBOL3读取拉丁文1中的文件并将其转换为UTF-8。是否有我可以使用的内置函数,或一些外部库?我在哪里可以找到它?

现在没有内置的,对不起。下面是一个简单的拉丁语-1到UTF-8转换的实现,这是我不久前编写并与Rebol 3一起使用的:

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的简洁的“拉丁-1字节值对应于Unicode码点”优化,这使得上面的内容压缩为一行(同时稍微不那么愚蠢)。还是。有关内存使用的更优化版本,请参阅@BrianH的漂亮答案。

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

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代码点具有相同数值的拉丁-1字符。如果要从另一个字符集进行转换,但情况并非如此,则可以在
b
上进行计算以重新映射字符

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

  • 通常,
    collect
    创建块。我们使用
    collect/into
    并将字符串作为目标传递给它。字符串使用的内存少于整数或字符块
  • 我们将字符串预先分配到输入数据的长度,这样可以节省重新分配的时间
  • 我们让Rebol的本机代码转换字符,而不是自己计算
  • 循环中的代码更少,因此它应该运行得更快
这个方法仍然一次将文件加载到内存中,并且仍然生成一个中间值来存储结果,但至少中间值更小。也许这会让你处理更大的文件


如果需要将其设置为UTF-8的原因是需要在Rebol中将文件作为字符串处理,只需将
跳过为二进制
并按原样返回字符串。或者您可以只处理二进制源数据,只需使用
将二进制中的字节转换为char函数,它为不属于有效utf-8序列的字节搜索二进制值。我们可以循环,直到找到并替换了所有这些值,然后将二进制值转换为字符串:

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
]
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
    ]
]
奖励:这里是上面的一个wee版本-
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]

@giuliolunati注意到,to binary数据是一个小包装器,它覆盖了我认为更好的表达式:to binary!数据。通过使用TO直接转换为数据类型,您可以编写类似于(条件[string!][binary!])数据的内容。为什么有些人认为他们一直用来指定类型的感叹号太刺耳,需要一个包装器,我不明白,但有些人确实这么认为。但是,如果您对某些东西是否内置感到好奇,您可以使用源代码,例如,从源代码到二进制代码,并查看它是否调用并且不是本机的!或者行动!旁注:我认为在这个时代,将输入文件本身转换为UTF-8是最好的主意……如果您的情况允许的话。使用下面的@earl方法很容易做到这一点,然后将其写出来。例如,写%myfile.utf8(拉丁文1-to-utf8读%myfile.latin1),那么你就不必关心转换的速度有多慢,因为你只做了一次…完全同意你的看法。我的所有文件都是utf8:-)但其他一些则不是:-(非常整洁,+1!特别是用于记住拉丁语1到代码点的对应关系。也是(capped)实用性的一个很好的例子)整数!值作为二进制的元素。发现“append”比collect/into快2倍。请参阅我的答案。与其他建议的变体不同,此版本不返回
二进制!
,而是返回
字符串!
(从技术上讲,它只将拉丁语1解码为内存中的Unicode,但不会将其再次编码为UTF-8)。要解决此问题,您必须将最后一行更改为“二进制”!t
。与其他建议的变体不同,此版本不返回“二进制”,而是返回一个
字符串!
(因此,从技术上讲,它只将拉丁语1解码为Unicode,但不会将其再次编码为UTF-8)。因此,对于真正的拉丁语-1到UTF-8转码,只需省去
到字符串的调用。
比其他调用快得多:修改版本9x,复制版本13x。太好了@rgchris!@earl我想我没有注意。正如你所说,在这两种情况下,删除
到字符串!
并返回二进制代码都很容易。谢谢链接:)