爆炸关于大写或数字字符的UTF8字符串

时间:2013-01-08 11:25:17

标签: php regex string utf-8 preg-replace

作为this question,我可以拆分包含大写字符串的字符串,如下所示:

function splitAtUpperCase($string){
    return preg_replace('/([a-z0-9])?([A-Z])/','$1 $2',$string);
}

$string = 'setIfUnmodifiedSince';
echo splitAtUpperCase($string);

输出为“如果未修改则设置”

但我需要一些修改:

  • 当字符串中存在这些字符时,该代码段不处理这些情况:ÇÖĞŞÜİ。我不想音译这些人物。然后我失去了言语的意义。我需要使用一些UTF字符。该代码使“HereÇonThen”变为“HereÇon然后”
  • 我也不想拆分大写缩写。如果单词是“IKnowYouWillComeASAPHere”,我需要将其转换为“我知道你会尽快到来”
  • 如果所有字母都是大写字母,请不要爆炸。喜欢“DONTCOMEHERE”
  • 也会分解数值。 “2013年之前”到“2013年结束前”
  • 如果第一个字符是散列键(#),则爆炸。

案例和预期结果

  1. “comeHEREtomorrow”=> “明天来这里”
  2. “KissYouTODAY”=> “今天吻你”
  3. “comeÜndeHere”=> “来Ünde这里”
  4. “NEVERSAYIT”=> “NEVERSAYIT”
  5. “2013willCome”=> “2013将来”
  6. “Before2013ends”=> “2013年结束前”
  7. “IKnowThat”=> “我知道”
  8. “#whatiknow”=> “#whatiknow”
  9. 对于这些情况,我使用后续的str_replace操作。我寻找一个简短的解决方案,不会为循环检查单词做太多。如果可能的话,最好将其作为preg_replace或等等。

    编辑:任何人都可以通过更改此PHP小提琴中的convert函数来尝试他的解决方案:http://ideone.com/9gajZ8

4 个答案:

答案 0 :(得分:2)

/([[:lower:][:digit:]])?([[:upper:]]+)/u应该这样做。

此处/u用于Unicode字符。并且([[:upper:]]+)用于上部套装字母的序列。

请注意。字母大小写取决于您使用的字符集。

答案 1 :(得分:2)

一些注意事项:

  • 使用Unicode properties搜索大写字母&小写字母(甚至是标题案例,f.ex。Dž Lj Nj Dz
  • comeHEREtomorrow& IKnowThat将无法使用一种方法,直到您使用一些词典查找确切的单词。

    因为如果您想将comeHEREtomorrow翻译为come HERE tomorrowIKnowThatIK now That(或甚至IK now T hat);

    如果您想将IKnowThat翻译为I Know ThatcomeHEREtomorrowcome H E R E tomorrow

我的解决方案:http://ideone.com/oALyTo(不包括非字母和非数字字符)

答案 2 :(得分:1)

嗯,I matched all of your test cases,但我仍然认为这不是一个好的解决方案。 (测试驱动设计中为数不多的缺陷之一)。

我采取了略微不同的方法。我没有尝试为单词之间的位置编写正则表达式,而是编写了一个正则表达式,查找显然是单词的所有内容,然后进行内爆。

function convert($keyword) {
   $wResult = preg_match_all('/(^I|[[:upper:]]{2,}|[[:upper:]][[:lower:]]*|[[:lower:]]+|\d+|#)/u', $keyword, $matches);
   return implode(' ',$matches[0]);
}

正如你所看到的,这就是我认为合格的一句话:

^I                 A capital I at the beginning of the string.  Break point: Icons.
[[:upper:]]{2,}    Consecutive capitals.  Break Point:  WellIKnowThat
[[:upper:]][[:lower:]]*    A single Capital followed by some lower case letters
[[:lower:]]+       A string of lower case letters
\d+                A string of digits
#                  A literal #

这并不完美 - 还有很多断点。你可以继续改进这些单词定义,但坦率地说,总会有一个你无法捕获的边缘情况。然后你慢慢地扩展这个正则表达式,直到它完全无法管理。您可以尝试使用字典,但最终也会崩溃。你怎么用“旋风”?还是“ITan”?这是“IT一个”,还是“I Tan”?举个例子? Here这是在我试图捕捉一些我的错误之后。它变得如此巨大,而且打破它打破的弦乐仍然是微不足道的。这个功能都是关于度数 - 花费多少时间来教你的算法所有世界语言的所有有趣点?

编辑:经过一些工作,决定我可以将其作为自己的单词分开,当且仅当它紧跟一个大写字母和一个小写字母后,I've updated my attempt at an answer.

function convert($keyword, $debug = false) {
   $wResult = preg_match_all('/I(?=[[:upper:]][[:lower:]])|[[:upper:]]{2,}|[[:upper:]][[:lower:]]*|[[:lower:]]+|\d+|#/u', $keyword, $matches);
   if($debug){
       var_dump($matches);
       var_dump($matches[0]);
       var_dump(implode(' ',$matches[0]));
   }
   return implode(' ',$matches[0]);
}

我还添加了一些新的测试用例:

 convert("Icons") = "Icons"
 convert("WellIKnowThat") == "Well I Know That"
 convert("ITan") == "I Tan"
 convert("whirlwind") == "whirlwind"

我认为这与今天的情况一样好。按优先顺序排列的最后一组“单词定义”是:

  1. 大写字母I,前面是大写字母和小写字母:I(?=[[:upper:]][[:lower:]])
  2. 两个或多个连续大写字母:[[:upper:]]{2,}
  3. 单个大写字母,后跟尽可能多的小写字母:[[:upper:]][[:lower:]]*
  4. 一个或多个连续小写字母:[[:lower:]]+
  5. 一个或多个连续数字:\d+
  6. 字面符号:#
  7. 我添加了另一个单词定义,一个测试用例,and refined the testing fiddle。新的字词定义与I的规则匹配,但与A匹配 - 英语中唯一的另一个字母单词。

答案 3 :(得分:0)

你需要Unicode正则表达式: \p{Lu} for upercase\p{Li} for lowercase

因此,您的用法将如下所示: /([\p{Ll}0-9])?([\p{Lu}])/