对于这个问题,我可以像这样拆分包含大写字母的字符串:
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年以前"
- 如果第一个字符是哈希键(#)则爆炸。
案例与预期结果
- "comeHERE etomtomorrow " => "comeHEREtomorrow"
- "KissYouTODAY" => "kiss you TODAY"
- "comeÜndeHere" => "来Ünde这里"
- " neversayit " => " neversayit "
- "2013willCome" => "2013willCome"
- "Before2013ends" => "Before2013ends"
- "IKnowThat" => "IKnowThat"
- "#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"
我想这是今天最好的了。按优先顺序排列的最后一组"单词定义"是:
- 大写I,前提是后面跟着一个大写字母和一个小写字母:
I(?=[[:upper:]][[:lower:]])
- 两个或两个以上连续的大写字母:
[[:upper:]]{2,}
- 一个大写字母,后面跟着尽可能多的小写字母:
[[:upper:]][[:lower:]]*
- 一个或多个连续小写字母:
[[:lower:]]+
- 一个或多个连续数字:
'd+
- 字号符号:
#
我添加了另一个单词定义,测试用例,并改进了测试工具。新的单词定义匹配I
的规则,但A
是英语语言中唯一的另一个单字母单词。
你需要Unicode正则表达式:'p{Lu} for upercase
和'p{Li} for lowercase
因此,你的用法看起来像这样:/(['p{Ll}0-9])?(['p{Lu}])/