使用 preg_replace() 将字母数字字符串从驼峰式命名法转换为短横线命名法

问题描述 投票:0回答:4

我现在有一个方法可以将我的camelCase字符串转换为kebab-case,但它分为三个调用

preg_replace()

public function camelToKebab($string, $us = "-")
{
    // insert hyphen between any letter and the beginning of a numeric chain
    $string = preg_replace('/([a-z]+)([0-9]+)/i', '$1'.$us.'$2', $string);
    // insert hyphen between any lower-to-upper-case letter chain
    $string = preg_replace('/([a-z]+)([A-Z]+)/', '$1'.$us.'$2', $string);
    // insert hyphen between the end of a numeric chain and the beginning of an alpha chain
    $string = preg_replace('/([0-9]+)([a-z]+)/i', '$1' . $us . '$2', $string);

    // Lowercase
    $string = strtolower($string);

    return $string;
}

我编写了测试来验证其准确性,并且它可以与以下输入数组一起正常工作(

array('input' => 'output')
):

$test_values = [
    'foo'       => 'foo',
    'fooBar'    => 'foo-bar',
    'foo123'    => 'foo-123',
    '123Foo'    => '123-foo',
    'fooBar123' => 'foo-bar-123',
    'foo123Bar' => 'foo-123-bar',
    '123FooBar' => '123-foo-bar',
];

我想知道是否有一种方法可以将我的

preg_replace()
调用减少到单行,这会给我相同的结果。有什么想法吗?

注意:参考这篇文章,我的研究向我展示了一个

preg_replace()
正则表达式,它可以让我几乎得到我想要的结果,除了它不适用于
foo123
的示例将其转换为
foo-123 
.

php regex preg-replace camelcasing kebab-case
4个回答
28
投票

您可以使用环视在单个正则表达式中完成所有这些操作:

function camelToUnderscore($string, $us = "-") {
    return strtolower(preg_replace(
        '/(?<=\d)(?=[A-Za-z])|(?<=[A-Za-z])(?=\d)|(?<=[a-z])(?=[A-Z])/', $us, $string));
}

正则表达式演示

代码演示

正则表达式说明:

(?<=\d)(?=[A-Za-z])  # if previous position has a digit and next has a letter
|                    # OR
(?<=[A-Za-z])(?=\d)  # if previous position has a letter and next has a digit
|                    # OR
(?<=[a-z])(?=[A-Z])  # if previous position has a lowercase and next has a uppercase letter

4
投票

这是我根据我之前标记的重复帖子的两分钱。这里接受的解决方案很棒。我只是想尝试用分享的内容来解决它:

function camelToUnderscore($string, $us = "-") {
    return strtolower(preg_replace('/(?<!^)[A-Z]+|(?<!^|\d)[\d]+/', $us.'$0', $string));
}

示例:

Array
(
    [0] => foo
    [1] => fooBar
    [2] => foo123
    [3] => 123Foo
    [4] => fooBar123
    [5] => foo123Bar
    [6] => 123FooBar
)

foreach ($arr as $item) {
    echo camelToUnderscore($item);
    echo "\r\n";
}

输出:

foo
foo-bar
foo-123
123-foo
foo-bar-123
foo-123-bar
123-foo-bar

说明:

(?<!^)[A-Z]+      // Match one or more Capital letter not at start of the string
|                 // OR
(?<!^|\d)[\d]+    // Match one or more digit not at start of the string

$us.'$0'          // Substitute the matching pattern(s)

在线正则表达式

问题已经解决了,所以我不会说我希望它有帮助,但也许有人会发现这很有用。


编辑

此正则表达式有限制:

foo123bar => foo-123bar
fooBARFoo => foo-barfoo

感谢@urban 指出这一点。这是他对这个问题发布的三个解决方案的测试链接:

三种解决方案演示


2
投票

来自同事:

$string = preg_replace(array($pattern1, $pattern2), $us.'$1', $string);
可能有用

我的解决方案:

public function camelToUnderscore($string, $us = "-")
{
    $patterns = [
        '/([a-z]+)([0-9]+)/i',
        '/([a-z]+)([A-Z]+)/',
        '/([0-9]+)([a-z]+)/i'
    ];
    $string = preg_replace($patterns, '$1'.$us.'$2', $string);

    // Lowercase
    $string = strtolower($string);

    return $string;
}

0
投票

您无需忍受大量环顾或多组模式的低效率来定位单词或连续数字之间的位置。

使用贪婪匹配找到所需的序列,然后用

\K
重置全字符串匹配,然后检查该位置是否不是字符串的末尾。 所有符合条件的内容都应该接收定界字符。 这种贪婪模式的速度在于它消耗一个或多个序列并且永远不会回头。

我会从我的答案中省略

strtolower()
电话,因为它只是挑战的噪音。

代码:(演示

preg_replace(
    '/(?:\d++|[A-Za-z]?[a-z]++)\K(?!$)/',
    '-',
    $tests
)

单词/数字之间的处理:

用户 步骤 图案 更换
阿努巴瓦 660
/(?<=\d)(?=[A-Za-z])|(?<=[A-Za-z])(?=\d)|(?<=[a-z])(?=[A-Z])
'-'
米克马库萨 337
/(?:\d++|[A-Za-z]?[a-z]++)\K(?!$)/
'-'

严格的驼峰处理:

用户 步骤 图案 更换
JazZ 321
/(?<!^)[A-Z]+|(?<!^|\d)[\d]+/
'-$0'
米克马库萨 250
/(?>\d+|[A-Z][a-z]*|[a-z]+)(?!$)/
'$0-'
米克马库萨 244
/(?:\d++|[a-z]++)\K(?!$)/
'-'

我对@Matt的答案打了折扣,因为它对每个字符串进行了三次完整的传递——就效率而言,它甚至不在同一个范围内。

© www.soinside.com 2019 - 2024. All rights reserved.