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


Explode UTF8 string regarding to uppercase or numeric characters

对于这个问题,我可以像这样拆分包含大写字母的字符串:

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

输出为"set If Unmodified Since"

但是我需要一些修改:

  • 当这些字符存在于string: ÇÖĞŞÜİ中时,该代码片段不处理这些情况。我不想把这些字符音译。然后我失去了单词的意义。我需要使用一些UTF字符。该代码使"HereÇonThen"变为"HereÇonThen"
  • 我也不想拆分大写缩写。如果文字是"IKnowYouWillComeASAPHere",我需要将其转换为"IKnowYouWillComeASAPHere"
  • 如果所有字母都是大写,不要爆炸。像"DONTCOMEHERE"
  • 也爆炸数值。"2013年以前"改为"2013年以前"
  • 如果第一个字符是哈希键(#)则爆炸。

案例与预期结果

  1. "comeHERE etomtomorrow " => "comeHEREtomorrow"
  2. "KissYouTODAY" => "kiss you TODAY"
  3. "comeÜndeHere" => "来Ünde这里"
  4. " neversayit " => " neversayit "
  5. "2013willCome" => "2013willCome"
  6. "Before2013ends" => "Before2013ends"
  7. "IKnowThat" => "IKnowThat"
  8. "#whatiknow" => "#whatiknow"

对于这些情况,我使用后续的str_replace操作。我寻找一个简短的解决方案,不需要太多的循环来检查单词。如果可能,最好是preg_replace或其他。

编辑:任何人都可以尝试他的解决方案通过改变convert函数在这个PHP提琴:http://ideone.com/9gajZ8

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

这里/u用于Unicode字符。([[:upper:]]+)用于大写字母序列。

。字母的大小写取决于所使用的字符集。

注意事项:

  • 使用Unicode属性搜索大写&小写字母(甚至是标题字母)。Dž Lj Nj Dz)
  • comeHEREtomorrow &IKnowThat不能用一种方法工作,除非你使用一些字典来找到确切的单词。

    因为如果你想把comeHEREtomorrow翻译成come HERE tomorrow, IKnowThat就会变成IK now That(甚至IK now T hat);

    如果你想把IKnowThat翻译成I Know That, comeHEREtomorrow就会变成come H E R E tomorrow

我的解决方案:http://ideone.com/oALyTo(不包括非字母&non-number特征)

嗯,我匹配了你所有的测试用例,但我仍然认为这不是一个好的解决方案。(测试驱动设计中为数不多的缺陷之一)。

我采取了一种稍微不同的方法。我没有尝试写一个正则表达式来确定单词之间的位置,而是写了一个正则表达式来查找所有明显是单词的地方,然后崩溃了。

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 #

它不是完美的-仍然有许多断点。你可以继续完善这些词的定义,但坦率地说,总会有一个你无法抓住的边缘情况。然后,您会慢慢地展开这个正则表达式,直到它完全无法管理。你可以试着用字典,但这最终也会失效。你用"whirlwind"做什么?或"ITan"?是"IT an"还是"I Tan"?一个恰当的例子?这是在我试图找出我的一些错误之后。它变得如此之大,要想出它断裂的弦仍然很简单。这个函数是关于度的——花多少时间教你的算法所有世界语言的有趣点是值得的?

编辑:经过一些工作,并且决定当且仅当它后面紧跟着一个大写字母和一个小写字母时,我可以作为一个单独的单词分离出来,我已经更新了我的答案尝试。

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. 字号符号:#

我添加了另一个单词定义,测试用例,并改进了测试工具。新的单词定义匹配I的规则,但A是英语语言中唯一的另一个单字母单词。

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

因此,你的用法看起来像这样:/(['p{Ll}0-9])?(['p{Lu}])/