此问题已在此处有答案:
Remove or match a Unicode Zero Width Space PHP(2个答案)
Remove or replace a ZERO WIDTH NON-JOINER character(2个答案)
5天前关闭。
我在匹配/替换编码为UTF8的ZWSP unicode时遇到一些问题
ZWSP: \x20\x0B
ZWSP (UTF8): \xE2\x80\x8B
作为一个额外的测试用例,我使用了NBSP(非中断空格),它可以按预期工作
所有preg_replace
都处于UTF8模式/u
- 当匹配NBSP时,它按预期工作。输入编码为UTF8,输出为空(NBSP unicode替换为空字符串)
- 当匹配ZWSP时,它仅在ZWSP输入是非UTF8编码的情况下工作。
- 如果您将ZWSP模式更改为UTF8编码版本并将输入保持为UTF8,则它也不工作
Q:那么如何在UTF8中匹配ZWSP?
还是这是一个bug?
代码
$nbsp = '\xA0'; // Non-breaking space
$zwsp = '\x20\x0B'; // Zero-width space
$zwsp_utf8 = '\xE2\x80\x8B';
$input_nbsp_utf8 = "\xC2\xA0";
$input_zwsp = "\x20\x0B";
$input_zwsp_utf8 = "\xE2\x80\x8B";
// NBSP
echo "NBSP\n-----\n";
echo "in: $input_nbsp_utf8--\nhex: ".bin2hex($input_nbsp_utf8)."\n";
$output = preg_replace('/'.$nbsp.'/u', '', $input_nbsp_utf8);
echo "out: $output--\nhex: ".bin2hex($output)."\n\n";
// ZWSP (input: **not** UTF8)
echo "ZWSP (input: **not** UTF8)\n-----\n";
echo "in: $input_zwsp--\nhex: ".bin2hex($input_zwsp)."\n";
$output = preg_replace('/'.$zwsp.'/u', '', $input_zwsp);
echo "out: $output--\nhex: ".bin2hex($output)."\n\n";
// ZWSP (input: UTF8)
echo "ZWSP (input: UTF8)\n-----\n";
echo "in: $input_zwsp_utf8--\nhex: ".bin2hex($input_zwsp_utf8)."\n";
$output = preg_replace('/'.$zwsp.'/u', '', $input_zwsp_utf8);
echo "out: $output--\nhex: ".bin2hex($output)."\n\n";
// ZWSP (pattern: UTF8, input: UTF8)
echo "ZWSP (pattern: UTF8, input: UTF8)\n-----\n";
echo "in: $input_zwsp_utf8--\nhex: ".bin2hex($input_zwsp_utf8)."\n";
$output = preg_replace('/'.$zwsp_utf8.'/u', '', $input_zwsp_utf8);
echo "out: $output--\nhex: ".bin2hex($output)."\n\n";
输出
NBSP
-----
in: --
hex: c2a0
out: --
hex:
ZWSP (input: **not** UTF8)
-----
in:
--
hex: 200b
out: --
hex:
ZWSP (input: UTF8)
-----
in: --
hex: e2808b
out: --
hex: e2808b // Output should be empty
ZWSP (pattern: UTF8, input: UTF8)
-----
in: --
hex: e2808b
out: --
hex: e2808b // Output should be empty
2条答案
按热度按时间wljmcqd81#
您可以使用ASCII模式正则表达式匹配UTF-8字符串-在本例中,您匹配的是单独的字节。如果您使用UTF-8模式的正则表达式,则输入必须是有效的UTF-8字符串。
匹配码点
输出:
PHP字符串
你可以使用
\u{XXXX}
来定义一个代码点。这只适用于双引号字符串。正如你在输出中看到的,模式包含UTF-8编码的实际unicode字符。字符类只显示一些空格。这也适用于输入字符串。它可以写成"NBSP: |\u{00A0}|, ZWSP: |\u{200B}|"
。PCRE码点定义
第二种模式是对码点使用PCRE语法:
\x{XXXX}
.\
应该在PHP字符串中进行转义(这里是一个后备,但显式总是好的)。您可以在模式输出中看到代码点定义。匹配字节数
您可以匹配字节。在这种情况下,正则表达式将不是UTF-8格式,输入字符串将被视为字节。这意味着您不能使用字符类-在这种模式下它们只能匹配单个字节。
输出:
xzlaal3s2#
像很多人一样,你似乎对UTF-8是什么感到困惑。UTF-8不是一个打开或关闭的设置,它是将文本转换为二进制数据的许多不同方法之一,并解释二进制数据以获取文本。
我不确定
\x20\x0B
来自哪里,或者它与任何事情有什么关系,但是说某个东西“不是UTF-8”就像说一个单词“不是法语”,或者一块肉“不是鸡肉”。忽略这一部分,让我们看看关键的代码:
您已经提供了
/u
修饰符,关于the manual says:模式和主题字符串被视为UTF-8。
然后,您使用
\xhh
表示法进行匹配,其中is described under escape sequences:在“\x”之后,最多读取两个十六进制数字(字母可以是大写或小写)。在UTF-8模式下,允许使用“\x{...}”,其中大括号的内容是十六进制数字的字符串。它被解释为UTF-8字符,其代码号是给定的十六进制数。原始的十六进制转义序列,\xhh,如果值大于127,则匹配双字节UTF-8字符。
这有点令人困惑,但它说通常情况下,
\xE2
将匹配二进制字节E2
,即11100010
;但如果/u
处于活动状态,它将与Unicode码位U+00E2
匹配,即“带扬抑符的拉丁小写字母a”。示例:
输出:
它 * 不会 * 匹配的是Unicode代码点
U+200B
,“零宽度空间”。因此,either 将字符串视为二进制,不要使用
/u
修饰符,并查找预期的字节串:[ Live Demo ]