Использование preg_replace() для преобразования CamelCase в snake_case
Теперь у меня есть метод, который преобразует мои строки с верблюжьим корпусом в случай змеи, но он разбит на три вызова preg_replace()
:
public function camelToUnderscore($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
.
Ответы
Ответ 1
Вы можете использовать lookarounds для выполнения всего этого в одном регулярном выражении:
function camelToUnderscore($string, $us = "-") {
return strtolower(preg_replace(
'/(?<=\d)(?=[A-Za-z])|(?<=[A-Za-z])(?=\d)|(?<=[a-z])(?=[A-Z])/', $us, $string));
}
Демо-версия RegEx
Демо-версия кода
RegEx Описание:
(?<=\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
Ответ 2
Вот мои два цента на основе дублированного сообщения, отмеченного мной ранее. Принятое решение здесь потрясающе. Я просто хотел попытаться решить его с помощью того, что было общим:
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 за это. Вот его ссылка на тесты с тремя решениями, размещенными по этому вопросу:
три демонстрации решений
Ответ 3
От коллеги:
$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;
}