如何在Rebol 3字符串中使用U + FFFF以上的Unicode代码点,如Rebol 2?

时间:2013-02-25 22:44:30

标签: unicode rebol rebol3 astral-plane rebol2

我知道你不能在Rebol 2中使用大于^(FF)的代码点在字符串中使用插入符号样式,因为它对Unicode没有任何了解。所以这并没有产生任何好处,它看起来搞砸了:

print {Q: What does a Zen master's {Cow} Say?  A: "^(03BC)"!}

然而,代码在Rebol 3中运行并打印出来:

Q: What does a Zen master's {Cow} Say?  A: "μ"!

那太好了,但是R3最大限度地发挥了它在U + FFFF中保持字符串的能力显然:

>> type? "^(FFFF)"
== string!

>> type? "^(010000)"
** Syntax error: invalid "string" -- {"^^(010000)"}
** Near: (line 1) type? "^(010000)"

当Rebol 2遇到它不知道的代码点时,情况要好于Rebol 2的随机行为。但是,如果您知道如何进行自己的UTF-8编码(或通过从磁盘加载源代码获取字符串),Rebol中常常会有一种解决方法来存储字符串。你可以从单个角色组装它们。

所以U + 010000的UTF-8编码是#F0908080,你可以说:

workaround: rejoin [#"^(F0)" #"^(90)" #"^(80)" #"^(80)"]

你会得到一个字符串,其中包含使用UTF-8编码的单个代码点,您可以在代码块中保存到磁盘并再次读回。 R3中有类似的技巧吗?

2 个答案:

答案 0 :(得分:4)

使用字符串有一种解决方法!数据类型也是如此。在这种情况下,您不能使用UTF-8,但您可以按如下方式使用UTF-16解决方法:

utf-16: "^(d800)^(dc00)"

,使用UTF-16代理项对^(10000)代码点进行编码。通常,以下函数可以执行编码:

utf-16: func [
    code [integer!]
    /local low high
] [
    case [
        code < 0 [do make error! "invalid code"]
        code < 65536 [append copy "" to char! code]
        code < 1114112 [
            code: code - 65536
            low: code and 1023
            high: code - low / 1024
            append append copy "" to char! high + 55296 to char! low + 56320
        ]
        'else [do make error! "invalid code"]
    ]
]

答案 1 :(得分:3)

是的,有一个技巧......这也是你应该在R2中使用的技巧。 不要使用字符串!使用二进制!如果你必须这样做:

  

好的解决方法:#{F0908080}

它可以在Rebol2中运行,它可以在Rebol3中运行。你可以保存它并加载它而没有任何有趣的业务。

事实上,如果完全关心Unicode,那么...... 停止使用高于^(7F)的代码点的字符串处理,如果你被困在Rebol 2而不是3 。我们会通过观察那个可怕的解决方法来了解原因:

  

糟糕的解决方法:重新加入[#“^(F0)”#“^(90)”#“^(80)”#“^(80)”]

... “你会得到一个包含单个UTF-8代码点的字符串” ......

你应该得到的唯一的是一个包含四个单独字符代码点的字符串,并带有4 = length? terrible-workaround。因为字符串,Rebol2坏了!与二进制基本没什么不同!引擎盖下。事实上,在Rebol2中,您可以在不复制的情况下来回对两种类型进行别名,查找AS-BINARY和AS-STRING。 (这在Rebol3中是不可能的,因为它们确实根本不同,所以不要附加到该功能!)

看到这些字符串报告的长度为4,这有点欺骗性,如果你转换它们to integer!,每个字符产生相同的值是错误的。因为如果你把它们写到某个地方的文件或端口,并且它们需要被编码,你就会被咬伤。请注意Rebol2:

>> to integer! #"^(80)"
== 128

>> to binary! #"^(80)"
== #{80}

但是在R3中,当需要二进制转换时,你有一个UTF-8编码:

>> to integer! #"^(80)"
== 128

>> to binary! #"^(80)"
== #{C280}

因此,当您看似正常工作的代码稍后会执行某些不同的操作时,您会感到惊讶,并且会以不同的方式进行序列化。事实上,如果你想知道R2在这方面是如何“搞砸了”,看看为什么你的“mu”有一个奇怪的符号。在R2中:

>> to binary! #"^(03BC)"
== #{BC}

它只是扔了“03”。 : - /

因此,如果您出于某种原因需要使用Unicode字符串并且无法切换到R3,请尝试使用类似于此的示例:

mu-utf8: #{03BC}
utf8: rejoin [#{} {Q: What does a Zen master's {Cow} Say?  A: "} mu-utf8 {"!}]

这会让你得到一个二进制文件。只将它转换为字符串以进行调试输出,并准备好看到乱码。但如果你被困在Rebol2中,这是正确的做法。

并重申答案:如果由于某些奇怪的原因而需要在Rebol3中使用那些更高的代码点,那么该怎么办:

utf8: rejoin [#{} {Q: What did the Mycenaean's {Cow} Say?  A: "} #{010000} {"!}]

如果我知道LINEAR B SYLLABLE B008 A是什么,我肯定这将是一个非常有趣的笑话。这让我更有可能说,如果你正在做一些这个深奥的,你可能只会引用一些代码点作为例子。您可以将大部分数据保存为字符串,直到您需要方便地插入它们,并将结果保存为二进制序列。


更新:如果遇到此问题,这里有一个实用程序功能,可以暂时解决它:

safe-r2-char: charset [#"^(00)" - #"^(7F)"]
unsafe-r2-char: charset [#"^(80)" - #"^(FF)"]
hex-digit: charset [#"0" - #"9" #"A" - #"F" #"a" - #"f"]

r2-string-to-binary: func [
    str [string!] /string /unescape /unsafe
    /local result s e escape-rule unsafe-rule safe-rule rule
] [
    result: copy either string [{}] [#{}]
    escape-rule: [
        "^^(" s: 2 hex-digit e: ")" (
            append result debase/base copy/part s e 16
        )
    ]
    unsafe-rule: [
        s: unsafe-r2-char (
            append result to integer! first s
        )
    ]
    safe-rule: [
        s: safe-r2-char (append result first s)
    ]
    rule: compose/deep [
        any [
            (either unescape [[escape-rule |]] [])
            safe-rule
            (either unsafe [[| unsafe-rule]] [])
        ]
    ]
    unless parse/all str rule [
        print "Unsafe codepoints found in string! by r2-string-to-binary"
        print "See http://stackoverflow.com/questions/15077974/"
        print mold str
        throw "Bad codepoint found by r2-string-to-binary"
    ]
    result
]

如果您使用此转换而不是to binary!转换,您将在Rebol2和Rebol3中获得一致的行为。 (它有效地实现了terrible-workaround样式字符串的解决方案。)