在PHP中将驼峰格式转换为可读格式,同时跳过缩写

9

我卡住了 - 我在这里看了很多答案,但似乎没有解决我的最后一个问题。

通过JSON API,我以camelcase格式接收到设备列表。我无法更改它。

我需要将这个camelcase转换成普通语言 -

到目前为止,我已经通过以下方式分割了大多数单词:

$string = "SomeEquipmentHere";

$spaced = preg_replace('/([A-Z])/', ' $1', $string);
var_dump($spaced);

string ' Some Equipment Here' (length=20)

$trimmed = trim($spaced);
var_dump($trimmed);
string 'Some Equipment Here' (length=19)

这个功能目前运行良好 - 但是在某些设备中包含缩写。

"ABSBrakes" - 这需要把 ABS 与 Brakes 分开来。

我不能检查连续几个大写字母,因为这样会将 ABS 和 Brakes 连在一起 - 还有更多类似的,比如:"CDRadio"。

所以我想要的输出结果是:

"ABS Brakes"
有没有一种方法可以格式化它,使得如果相邻的字母都是大写,则只在该序列的最后一个大写字母之前添加一个空格? 我不擅长正则表达式。 编辑 两种解决方案都很棒——以后来到这里的人应该阅读两个答案。 最后需要解决的问题是以下模式: "ServiceOK" 变成 "Service O K" "ESP" 变成 "ES P" 仅由纯大写缩写组成的模式通过计算小写字母的函数进行修复,如果没有小写字母,则会跳过 preg_replace()。 但是,正如 Flying 在他的答案评论中所写,他的正则表达式可能无法覆盖许多实例,答案可能是不可能的——我不知道这是否对正则表达式构成了挑战。 可能通过添加一些“如果大写字母后面没有小写字母,则不应插入空格”的规则来解决。

任何正经的API都应该发送一个标识符(数字或驼峰式对象名称)和一个“显示名称”。除了你的解决方法,我会联系API所有者并要求他提供缺失的信息。 - Daniel W.
@DanFromGermany 这是真的... 但是我花了将近一周的时间才从他们那里获得了访问权限,所以我怀疑这个问题很快就会被解决。 - Stender
2个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
4
这里是一种不使用任何锚点、捕获组或替换字符串中的引用的单调用模式:/(?:[a-z]|[A-Z]+)\K(?=[A-Z]|\d+)/ Pattern&Replace Demo 代码:(Demo)
$tests = [
    'SomeEquipmentHere',
    'ABSBrakes',
    'CDRadio',
    'Valve14',
];
foreach ($tests as $test) {
    echo preg_replace('/(?:[a-z]|[A-Z]+)\K(?=[A-Z]|\d+)/',' ',$test),"\n";
}

输出:

Some Equipment Here
ABS Brakes
CD Radio
Valve 14

这是更好的方法,因为没有什么需要清理。 如果有新的字符串需要考虑(打破我的方法),请在评论中留下它们,以便我可以更新我的模式。

模式说明:

/         #start the pattern
(?:[a-z]  #match 1 lowercase letter
|         #or
[A-Z]+)   #1 or more uppercase letters
\K        #restart the fullstring match (forget the past)
(?=[A-Z]  #look-ahead for 1 uppercase letter
|         #or
\d+)      #1 or more digits
/         #end the pattern

编辑:

还有一些其他的模式可能会提供更好的准确性,包括:

/(?:[a-z]|\B[A-Z]+)\K(?=[A-Z]\B|\d+)/

虽然上述模式无法正确处理ServiceOK

演示链接 单词边界链接


或者使用带有锚点的此模式:

/(?!^)(?=[A-Z][a-z]+|(?<=\D)\d)/
上述模式将准确地拆分:SomeEquipmentHereABSBrakesCDRadioValve14ServiceOKESP,正如OP所要求的那样。 *注意:提供更多示例字符串可以提高模式准确性。 演示链接

@Stender 这个有趣的问题已解决。我为您提供了一种更清晰的方法,适用于所有提供的输入。如果您有任何新的输入,而我的模式不能正确地处理它们,请将它们添加到您的问题中并留下评论。如果您需要我进一步解释任何内容,只需询问即可。 - mickmackusa
这看起来非常干净!我有一个新的模式问题,不知道是否可以解决 - 基本上:如果大写字母后面没有小写字母,则不应插入空格('-')- 我已更新问题。 - Stender
你能否像这个链接中的例子一样提供更多的字符串?提供的字符串越多,我就能够更好地优化模式。 - mickmackusa
@mickmacusa 我已经测试了迄今从API获取的所有字符串,并对你更新的正则表达式进行了测试 - 没有任何东西破坏它 - 这非常有帮助。 它甚至让我免于使用我编写的小写计数函数! - Stender
我已经更新了我的答案,包括了一些模式。最后一个可能是你的项目中最好的选择。如果你想编写一个可以跳过 preg_replace() 调用的条件,你可以使用 ctype_upper() - mickmackusa
显示剩余2条评论

3

以下是解决方法:

$tests = [
    'SomeEquipmentHere',
    'ABSBrakes',
    'CDRadio',
    'Valve14',
];
foreach ($tests as $test) {
    echo trim(preg_replace('/\s+/', ' ', preg_replace('/([A-Z][a-z]+)|([A-Z]+(?=[A-Z]))|(\d+)/', '$1 $2 $3', $test)));
    echo "\n";
}

regex101相关的测试。

更新:添加了一个额外问题的示例。


这正是我一直在寻找的!您,先生/女士真棒——我知道这不是问题的一部分——但您能否在正则表达式中添加一些内容,例如在字符串中第一个数字之前加空格?所以像Valves14这样的字符串也可以有空格吗? - Stender
@Stender 这是一种略有不同的方法,但我已经更新了答案,为这样的字符串提供了解决方案。 - Flying
我是不是读错了,或者在你的正则表达式测试中,test2现在返回“AB SB rakes”? - Stender
2
@MartinLyder 当然,可能会有很多不同的情况超出了这个问题的范围,例如,可能需要保留单词的一部分以及数字或其他内容。但是,如果没有完整的此类情况列表,就不太可能提供解决方案。这就是为什么我提供了测试列表和regex101链接作为答案的原因。 - Flying
我可以确认它确实会这样做,但我将对该函数进行限制,只有在存在一个或多个小写字母时才运行。 - Stender

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,