人类会认为以下字符串表示相同的文本:
>>> [b'axc3xa9b'.decode('utf-8'), b'aexccx81b'.decode('utf-8')]
['aéb', 'aéb']
一个字符串使用预先组合的字符,另一个字符串使用分解的字符,但结果文本是相同的。
但是,ICU似乎不支持匹配预合成和分解的字符:
UParseError pe;
UErrorCode status;
URegularExpression *regexp = uregex_open(L"au0065u0301b", 4, URegexpFlag::UREGEX_CASE_INSENSITIVE, &pe, &status);
uregex_setText(regexp, L"au00E9b", 3, &status);
UBool success = uregex_matches(regexp, 0, &status); // fails
uregex_close(regexp);
PCRE:
$ LC_ALL=en_US.UTF-8 pcretest <(printf '%sn' $'/aexccx81b/i' $'axc3xa9b')
PCRE version 8.45 2021-06-15
/aéb/
aéb
No match
所以,如果我想处理任意正则表达式的这种情况(以便结果对人类有意义)…那我该怎么做呢?是否有一种方法可以使现有的库对任意Unicode正则表达式正确工作&全集吗?
是否存在可以完全处理Unicode的regex库(除了完整的案例折叠等)?
理想情况下,我希望我可以很容易地在c++中使用,但其他语言的解决方案是受欢迎的。
如果要比较两个字符串是否等价,而不管它们的归一化形式如何,ICU有unorm_compare()
。例子:
// g++ -Wall -Wextra -O $(icu-config --cppflags --cxxflags --ldflags --ldflags-icuio) -o demo demo.cpp
#include <cstdlib>
#include <iostream>
#include <unicode/unorm2.h>
#include <unicode/unistr.h>
#include <unicode/ustream.h>
int main(void) {
UErrorCode err = U_ZERO_ERROR;
icu::UnicodeString a{u"au0065u0301b"}, b{u"au00E9b"};
int res = unorm_compare(a.getBuffer(), a.length(),
b.getBuffer(), b.length(),
U_COMPARE_IGNORE_CASE, &err);
if (U_FAILURE(err)) {
std::cerr << "unorm_compare() error: " << u_errorName(err) << 'n';
return EXIT_FAILURE;
}
if (res == 0) {
std::cout << a << " and " << b << " are equivalent.n";
} else {
std::cout << a << " and " << b << " differ.n";
}
}
在编译和运行时,输出
aéb and aéb are equivalent.