Unicode 如何在U+;Rebol 3字符串中的FFFF与Rebol 2中的相同?

Unicode 如何在U+;Rebol 3字符串中的FFFF与Rebol 2中的相同?,unicode,rebol,rebol3,astral-plane,rebol2,Unicode,Rebol,Rebol3,Astral Plane,Rebol2,我知道在REBOL2中不能对大于^(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时最大限度地发挥了在字符串中保存字符的能力:

我知道在REBOL2中不能对大于^(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)"
这种情况比REBOL2遇到它不知道的代码点时的随机行为要好得多。但是,如果您知道如何进行自己的UTF-8编码(或者通过从磁盘加载源代码来获取字符串),Rebol中曾经有一个用于存储字符串的变通方法。你可以从单个字符中组合它们

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

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

您将得到一个字符串,其中包含使用UTF-8编码的单个代码点,您可以将其以代码块的形式保存到磁盘,然后再次读取。R3中有类似的技巧吗?

是的,有一个技巧……这也是你应该在R2中使用的技巧。不要用绳子!使用二进制!如果你必须做这类事情:

好的解决方法:#{F0908080}

它可以在Rebol2中工作,也可以在Rebol3中工作。您可以保存并加载它,而无需任何有趣的操作

事实上,如果您根本不关心Unicode,那么如果您陷入Rebol 2而不是Rebol 3,请停止使用高于^(7F)的代码点的字符串处理。我们将通过查看可怕的解决方法来了解原因:

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

…“你会得到一个带有单个UTF-8码点的字符串”

唯一应该得到的是一个具有四个独立字符代码点的字符串,并且
4=长度?糟糕的解决方法
。Rebol2已断开,因为字符串!基本上与二进制没有区别!在引擎盖下。事实上,在Rebol2中,您可以在不复制的情况下来回别名这两种类型,查找AS-BINARY和AS-STRING。(这在Rebol3中是不可能的,因为它们实际上是根本不同的,所以不要附加到该功能上!)

看到这些字符串报告长度为4有点欺骗性,如果将它们
转换为整数,则每个字符产生相同的值是一种错误的安慰。因为如果你将它们写入某个文件或端口,并且需要对它们进行编码,你就会被咬。在Rebol2中注意这一点:

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

>> to binary! #"^(80)"
== #{80}
但在R3中,当需要进行二进制转换时,可以使用UTF-8编码:

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

>> to binary! #"^(80)"
== #{C280}
因此,当您的看似正常工作的代码在以后做了一些不同的事情,并最终以不同的方式序列化时,您会感到惊讶。事实上,如果你想知道R2在这方面有多“混乱”,看看为什么你的“mu”有一个奇怪的符号。在R2中:

它只是把“03”扔掉了-/

因此,如果您出于某种原因需要使用Unicode字符串,但无法切换到R3,请在cow示例中尝试以下操作:

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} {"!}]
如果我知道那是什么,我相信那会是一个非常有趣的笑话。这让我想说的是,如果你在做一件如此深奥的事情,你可能只引用了几个代码点作为例子。您可以将大部分数据作为字符串保存,直到需要方便地将它们插入,并将结果保存在二进制序列中


更新:如果遇到此问题,以下是一个实用函数,可用于暂时解决此问题:

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
]

如果你用这个而不是
来表示二进制转换,您将在Rebol2和Rebol3中获得一致的行为。(它有效地实现了
糟糕的解决方案
样式字符串)。

有一个使用字符串的解决方案!数据类型也是如此。在这种情况下不能使用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"]
    ]
]
utf-16:func[
代码[整数!]
/本地低-高
] [
案例[
代码<0[确实出错!“无效代码”]
代码<65536[将副本“”附加到字符!代码]
代码<1114112[
代码:代码-65536
低:代码和1023
高:代码-低/1024
将复制“”追加到字符!高+55296追加到字符!低+56320
]
'else[确实出错!“无效代码”]
]
]
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"]
    ]
]