【问题标题】:Split string into array based on a unicode character range in PHP根据PHP中的Unicode字符范围将字符串拆分为数组
【发布时间】:2017-04-25 11:23:18
【问题描述】:

对不起,这个模棱两可的主题,我正在寻找的是一个带有西里尔字符的字符串,可能会像

«Добрый день!» - сказал он, потянувшись…

放入一个类似的数组

[0] => «
[1] => Добрый␠
[2] => день!»␠-␠
[3] => сказал␠
[4] => он,␠
[5] => потянувшись…

所以本质上,我正在寻找在任何字符和西里尔字符([а-я] 范围)之间的边界上发生的中断,尽管只有当我们从任何字符转换为西里尔字符时这才必须是真的,而不是反之亦然。我见过用标点符号和拉丁字母成功解决这个问题的例子

preg_split('/([^.:!?]+[.:!?]+)/', 'hello:there.everyone!so.how?are:you', NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );

但到目前为止,我尝试将其改造成不同的东西都失败了:

preg_split ('/(?<=[^а-я])/ius', $text, NULL, PREG_SPLIT_NO_EMPTY);

几乎可以工作,但它也会被空格和标点符号等常规字符分割,这不是我想要的。显然我的正则表达式有问题。我应该如何修改它以获得如上示例中的结果?

【问题讨论】:

  • 为什么« 字符被捕获为一个单独的项目,而相同的相反» 被捕获为字符串день!».. 的一部分?
  • 是的,这并不是最好的例子,我愿意以某种方式牺牲那里的 [0]。

标签: php arrays regex unicode split


【解决方案1】:

如果下一个字符是西里尔字符,您还必须向前看。这段代码可以完成这项工作:

$t = preg_split ('/(?<=[^а-я])(?=[а-я]+)/ius', $text, NULL, PREG_SPLIT_NO_EMPTY);

它给出了这个输出:

Array
(
    [0] => «
    [1] => Добрый 
    [2] => день!» - 
    [3] => сказал 
    [4] => он, 
    [5] => потянувшись…
)

Here you can try it.

【讨论】:

  • 谢谢您,但我认为您还应该查看 bobble bubble 的答案,这似乎更优雅一些。
  • 已经为此投票。另一个变种:$res = preg_split('/\b(?=[а-я])/iu', $str);
  • 同样的故事。我的 PHP 不尊重某些东西(尽管我不明白它为什么会这样做)并且只有前瞻变体有效。
【解决方案2】:

试试这个正则表达式:[\x{0400}-\x{04FF}]*[^\x{0400}-\x{04FF}]*。从 0400 到 04FF 的所有 unicode 字符都被视为西里尔字母。它应该与您想要的完全匹配。您还可以按照另一个答案中的建议将\x{0400}-\x{04FF} 替换为\p{Cyrillic}

这是该范围内的所有字符:
ѐёђѓєїјљњћќѝўџ0абвгдежзийклмноп0рстуфхцчэыьюя0абвгдежзийклмнопрстуфхцчэыьюяѐёђѓєѐёђѓєїјљњћќѝўџ0460ѡѡѣѣѥѥѧѧѩѩѫѫѭѭѯѯѱѱѳѳѵѵѷѷѹѹѻѻѽѽѿѿҁҁ҂҃҄҅҆҇҈҉ҋҋҍҍҏҏґґғғҕҕҗҗҙҙққҝҝҟҟҡҡңңҥҥҧҧҩҩҫҫҭҭүүұұҳҳҵҵҷҷҹҹһһҽҽҿҿ04c0ӏӂӂӄӄӆӆӈӈӊӊӌӌӎӎӏ04d0ӑӑӓӓӕӕӗӗәәӛӛӝӝӟӟӡӡӣӣӥӥӧӧөөӫӫӭӭӯӯ04f0ӱӱӳӳӵӵӷӷӹӹӻӻӽӽӿӿ

【讨论】:

  • 这个正则表达式在我尝试时会丢失所有其他单词,只有奇数单词会进入数组,偶数单词会丢失。
  • 不要与 split 一起使用,与 match 一起使用。这匹配一个字符串而不是一个要拆分的位置。
【解决方案3】:

使用以下正则表达式解决方案:

$s = "«Добрый день!» - сказал он, потянувшись…";
$res = preg_split('/\b(\p{Cyrillic}+\W*)/u', $s, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
print_r($res);
// Array(
//   [0] => «
//   [1] => Добрый 
//   [2] => день!» - 
//   [3] => сказал 
//   [4] => он, 
//   [5] => потянувшись…
//)

PHP demo

详情

  • \b(\p{Cyrillic}+\W*) - 匹配并捕获整个 Cyrillic 单词,其后有 0+ 个非单词字符
  • 模式用捕获括号包裹,PREG_SPLIT_DELIM_CAPTURE 会将捕获的值推送到结果数组中
  • PREG_SPLIT_NO_EMPTY 将丢弃数组中的空值
  • /u 修饰符将使 \b(单词边界)和 \W 识别 Unicode,并允许使用正则表达式处理 Unicode 字符串。

【讨论】:

  • 我真的很喜欢这个优雅的解决方案,但是当我在自己的 PHP 中尝试它时,我得到的只是一行,没有拆分。不过,它确实适用于您的演示。为什么会这样?
【解决方案4】:

如何在初始 \b word boundaryu modifier 处拆分。

$res = preg_split('/\b(?=\w)(?!^)/u', $str);

lookahead 确保\b 后跟word character(?!^) 防止空匹配 if start

See this demo at eval.in

【讨论】:

  • 这是一个合乎逻辑的解决方案,但不幸的是,我需要仅在西里尔字符上出现中断,以便例如“слово word”不会分成两部分。
  • @ЗахарJoe 在这种情况下你可以试试$res = preg_split('/\b(?=[^\Wa-z])/iu', $str);
  • 我刚刚尝试了您提供的两个正则表达式,不幸的是我的 PHP (5.5.38) 版本由于某种原因在这两种情况下都只返回一个数组元素。
  • @ЗахарJoe 可能与 preg_split('/\b(?=\p{Cyrillic})/u', $str); 类似 Wiktor 的答案相同。
  • 可能是这样,而且有点莫名其妙。我确实设置了 mb_internal_encoding ( 'UTF-8' );而且我认为它不需要任何其他技巧。想知道什么坏了,在哪里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多