使用string.sub函数处理非英文字符时出现问题

4

我需要获取文本变量的第一个字符。以下是实现此操作的简单方法之一:

string.sub(someText,1,1)

或者

someText:sub(1,1)

如果我执行以下操作,我希望得到 'ñ' 作为第一个字母。然而,任何一个 sub 方法的结果都是 'Ã'
local someText = 'ñññññññ'
print('Test whole: '..someText) 
print('first char: '..someText:sub(1,1))
print('first char with .sub: '..string.sub(someText,1,1))

这是控制台的结果:

2014-03-02 09:08:47.959 Corona Simulator[1701:507] Test whole: ñññññññ
2014-03-02 09:08:47.960 Corona Simulator[1701:507] first char: Ã
2014-03-02 09:08:47.960 Corona Simulator[1701:507] first char with .sub: Ã

看起来 string.sub() 函数对返回的值进行了UTF-8编码。我尝试使用 Corona SDK 提供的 utf8_decode() 函数,但并没有成功。模拟器显示该函数期望得到一个数字,但得到了 nil
我还在网上搜索了一下,看看是否有其他人遇到过这个问题。我发现Lua、Corona、Unicode和UTF-8有很多讨论,但没有找到任何可以解决这个具体问题的内容。

string.sub函数将返回值编码为UTF-8”—只有在源数据编码为UTF-8的情况下才会出现这种情况。没有标准的Lua库会更改编码方式。无论如何,你必须确保了解所有处理的字符串数据的字符集和编码方式(尽管通常只需知道它是系统默认值即可)。 - Tom Blodget
2个回答

5
Lua字符串是8位清洁的,这意味着Lua中的字符串是字节流。UTF-8字符ñ有多个字节,但是someText:sub(1,1)仅返回第一个单一字节。
对于UTF-8编码,ASCII范围内的所有字符与ASCII中的表示相同,即小于128的单个字节。对于其他CodePoints,字节序列的第一个字节在194-244范围内,连续的字节在128-191范围内。
因此,您可以使用模式".[\128-\191]*"来匹配单个UTF-8 CodePoint(而不是Grapheme):
for c in "ñññññññ":gmatch(".[\128-\191]*") do -- pretend the first string is in NFC
    print(c)
end

输出:

ñ
ñ
ñ
ñ
ñ
ñ
ñ

谢谢您的回复。这是非常好的信息。再帮我一下就太感谢了。有什么想法可以帮我分割剩余的文本吗?这个模式匹配只能找到第一个字母。我想要能够正确地分割剩余的字母并将它们显示出来。 - C. Ulker
@C.Ulker 你可以使用 string.gmatch() 来分割字符串,详见更新。 - Yu Hao
@TomBlodget 真的吗?我一直认为ASCII是如此广为人知,以至于每个程序员都知道它是什么。无论如何,我添加了维基百科链接以防万一。 - Yu Hao
我的意思是“太多人”知道 ASCII。他们认为他们在使用它,但实际上并不是,因此会遇到像问题提问者一样的问题。 - Tom Blodget
@余浩。感谢你的帮助。 - C. Ulker
显示剩余5条评论

0
关于使用的字符集: 只需知道您在自己的代码中需要什么要求,并确保这些要求实际上得到满足。 有各种典型的要求:
  • ASCII兼容(又称任何字节<128表示ASCII字符,所有ASCII字符都表示为它们自己)
  • 固定大小与可变宽度(可能是自同步)字符集
  • 没有嵌入的0字节
编写代码时,尽量避免需要这些要求中的尽可能少的要求,并对其进行文档化。
匹配单个UTF-8字符:请确定您所指的UTF-8字符的含义。是字形还是码点?据我所知,您需要完整的Unicode表才能进行字形匹配。您真的需要达到这个级别吗?

Lua字符串中的0字节与其他字节值一样处理。它不是需要避免的东西。数据就是数据,Lua让它自由自在。 - Tom Blodget
实际上,目前(5.2)并非所有的Lua库函数都是完全0清洁的。比较io.lines / file:lines / io.read / file.read和format l或L(std format)。这就是我列出第3点的唯一原因,没有免责声明“不适用于Lua”。此外,我之所以添加了这个不同的解决方案,是因为我无法在上面进行评论,而第一个解决方案是具有欺骗性的错误。 - Deduplicator
是的,我的Unicode知识相当有限,我看到了你对我的答案提出的建议修改,包括那些未被批准的。我很想看看你对这个问题的解决方案,我的意思是,写一些代码让我们看看你的意思。 - Yu Hao
既然您现在无法对我的答案进行评论,您可以在这里留言和@我,我会在看到问题所在后更新我的答案。 - Yu Hao
好的,简单来说:Unicode描述了CodePoints以及它们如何组合/比较/排序,以及多种编码方式。使用Unicode规则,一个字符可以由多个CodePoints组成。您的模式确实匹配了一个作为UTF-8编码的单个CodePoint,如果您运气好(或不幸?),这个CodePoint可能是您测试数据中的完整字符。但不能保证。目前http://en.wikipedia.org/wiki/Combining_character列出了四个包含组合CodePoints的不同Unicode范围。 - Deduplicator
续:顺便说一句,这还不是全部。当你加入印度、中文、希伯来语和其他文字时,情况会变得更加混乱。那时你真的必须按照字形簇而不是字形进行分割,因为这更接近于字符。我不会深入探讨这个问题,因为我不太理解它。所以,如果你真的需要,就选择一个能够处理这个问题的库,或者认真学习Unicode。祝你好运。 - Deduplicator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接