如何在C++字符串中搜索非ASCII字符?

3
string s="x1→(y1⊕y2)∧z3";

for(auto i=s.begin(); i!=s.end();i++){
    if(*i=='→'){
       ...
    }
} 

比较字符的方法肯定是错误的,正确的做法是什么?我正在使用vs2013。

2
我十分确定你需要使用宽字符来完成这个任务。 - NathanOliver
1
你需要决定正在使用的字符大小和编码。 - Hot Licks
我也有VS2013,sizeof('→')给我的结果是1而不是4。我也收到了一个关于常量的警告。当我使用L'→'(宽字符)时,警告消失了,所以这可能是正确的方法。 - user1610015
wchar_t 对于 Unicode 字符 U+FFFF 以上的字符也会有同样的问题。 - roeland
是的,但是它不在 FFFF 之上。此外,编译器会在您尝试将字符放入无法容纳该字符的常量中时发出警告。 - user1610015
显示剩余7条评论
2个回答

3

首先,您需要一些基本了解程序如何处理Unicode的知识。否则,您应该阅读一下,我很喜欢Joel on Software上的这篇文章

实际上,您有两个问题:

问题#1:将字符串放入程序中

您的第一个问题是将实际字符串放入string s中。根据源代码文件的编码方式,MSVC可能会破坏该字符串中的任何非ASCII字符。

  • 要么将C++文件保存为UTF-16(Windows混淆地称为 Unicode ),并使用whcar_twstring(有效地将表达式编码为UTF-16)。保存为带BOM的UTF-8也可以。任何其他编码,您的L"..."字符文字将包含错误的字符。

    请注意,其他平台可能将wchar_t定义为4个字节而不是2个字节。因此,处理U + FFFF以上的字符将是不可移植的。

  • 在所有其他情况下,您不能只是在源文件中写入那些字符。最具可移植性的方法是将字符串文字编码为UTF-8,对于所有非ASCII字符,使用\ x 转义代码。就像这样:"x1 \ xe2 \ x86 \ x92a \ xe2 \ x8a \ x95"“b)”而不是"x1→(a⊕b)"

    是的,那就是最难以阅读和繁琐的。根本问题是MSVC实际上并不支持使用UTF-8。您可以通过这里的问题概述: 如何在Visual C ++ 2008中创建UTF-8字符串文字

    但是,请考虑这些字符串在源代码中实际显示的频率。

问题#2:查找字符

(如果您正在使用UTF-16,则可以找到L'→'字符,因为该字符可以表示为一个whcar_t。对于U + FFFF以上的字符,您必须使用下面的宽版本解决方法。)

无法定义代表箭头字符的char。但是,您可以使用字符串:"\xe2\x86\x92"。(那是一个带有3个箭头字符和\0终止符的字符串。

现在,您可以在表达式中搜索此字符串:

s.find("\xe2\x86\x92");

UTF-8编码方案保证了总是能找到正确的字符,但请记住这是一个字节偏移量。

std::string 不太适用于 UTF-8。如果您调用 s.find(...) 并且有其他多字节字符在您搜索的内容之前,它将给出错误的索引。实际上,您只是把这个问题复杂化了。如果 OP 的代码被简单地转换为使用宽字符,它可以原封不动地工作。 - user1610015
取决于你是否想在其他平台上编译应用程序,以及是否可以允许它在星际字符(U+FFFF以上的字符)上中断。Windows上的wchar_t不能编码任何Unicode字符。 - roeland
@user1610015,使用宽字符并不能解决问题#1。如果您只是写L"a→b",您可能会惊讶于实际上会出现在您的字符串中的内容。 - roeland
我不知道你的意思。L"a→b" 包含字符 a、→ 和 b。是的,它可能在一些罕见的平台上无法工作,但这几乎是 C++ 中任何事情的情况。Visual Studio 会在字符无法编码时发出警告,因此不可能有“意外”。 - user1610015
它包含字符a、→和b,仅当编译器知道源文件的编码时。因此,Microsoft Visual Studio在保存UTF-8时始终使用BOM。然而,在Linux C++文件通常以UTF-8格式保存,没有BOM(我认为g++曾经对该BOM感到困扰)。如果您使用Visual Studio编译这样的文件,则它会假定为ANSI编码,然后您的字符串将包含(在设置为英语的系统上)L"aâ\u0086\u0092b" - roeland

1

我的评论太长了,所以我把它提交为一个答案。

问题在于每个人都集中于Unicode可能使用的不同编码(UTF-8、UTF-16、UCS2等)的问题。但你在这里的问题将刚刚开始。

还有一个复合字符的问题,这将真正混乱你尝试进行的任何搜索。

假设你正在寻找字符'é',你在Unicode中找到它为U+00E9并进行搜索,但不能保证这是表示此字符的唯一方法。该文档还可能包含U+0065 U+0301组合。它实际上是完全相同的字符。

是的,不仅是“看起来一样”的字符,而且确实是相同的字符,因此任何软件甚至一些编程库都会自由地转换一个字符到另一个字符,甚至不告诉你。

因此,如果你希望进行强大的搜索,你需要表示不仅Unicode的不同编码,而且字符本身,包括复合字符和已经准备好的字符之间的相等性。


是的,说得好。此外,如果您在 [ U+0065 U+0301 U+2192 ] 中找到箭头,则会发现它是第三个代码点,但在显示时它是第二个字形。 - roeland

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