Unicode 如何在U+;Rebol 3字符串中的FFFF与Rebol 2中的相同?
我知道在REBOL2中不能对大于^(FF)的代码点使用插入符号样式的字符串转义,因为它对Unicode一无所知。所以这并没有产生任何好的结果,它看起来一团糟: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时最大限度地发挥了在字符串中保存字符的能力:
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"]
]
]