Разбить слово camelCase на слова с помощью php preg_match (Регулярное выражение) - PullRequest
62 голосов
/ 23 декабря 2010

Как бы я разбил слово:

oneTwoThreeFour

на массив, чтобы я мог получить:

one Two Three Four

с preg_match?

Я устал от этого, но это просто дает целое слово

$words = preg_match("/[a-zA-Z]*(?:[a-z][a-zA-Z]*[A-Z]|[A-Z][a-zA-Z]*[a-z])[a-zA-Z]*\b/", $string, $matches)`;

Ответы [ 12 ]

75 голосов
/ 23 декабря 2010

Вы также можете использовать preg_match_all как:

preg_match_all('/((?:^|[A-Z])[a-z]+)/',$str,$matches);

Пояснение:

(        - Start of capturing parenthesis.
 (?:     - Start of non-capturing parenthesis.
  ^      - Start anchor.
  |      - Alternation.
  [A-Z]  - Any one capital letter.
 )       - End of non-capturing parenthesis.
 [a-z]+  - one ore more lowercase letter.
)        - End of capturing parenthesis.
69 голосов
/ 23 декабря 2010

Вы можете использовать preg_split как:

$arr = preg_split('/(?=[A-Z])/',$str);

Посмотреть это

Я в основном разбиваю входную строку непосредственно передзаглавная буква.Используемое регулярное выражение (?=[A-Z]) соответствует точке непосредственно перед заглавной буквой.

50 голосов
/ 11 октября 2011

Я знаю, что это старый вопрос с принятым ответом, но ИМХО есть лучшее решение:

<?php // test.php Rev:20140412_0800
$ccWord = 'NewNASAModule';
$re = '/(?#! splitCamelCase Rev:20140412)
    # Split camelCase "words". Two global alternatives. Either g1of2:
      (?<=[a-z])      # Position is after a lowercase,
      (?=[A-Z])       # and before an uppercase letter.
    | (?<=[A-Z])      # Or g2of2; Position is after uppercase,
      (?=[A-Z][a-z])  # and before upper-then-lower case.
    /x';
$a = preg_split($re, $ccWord);
$count = count($a);
for ($i = 0; $i < $count; ++$i) {
    printf("Word %d of %d = \"%s\"\n",
        $i + 1, $count, $a[$i]);
}
?>

Обратите внимание, что это регулярное выражение (как и решение '/(?=[A-Z])/' от codaddict - которое работает как шарм для правильно сформированных слов camelCase), соответствует только позиции в строке и вообще не потребляет текст. Это решение имеет дополнительное преимущество, заключающееся в том, что оно также работает корректно для не очень хорошо сформированных псевдо-верблюжьих слов, таких как: StartsWithCap и: hasConsecutiveCAPS.

Введите:

oneTwoThreeFour
StartsWithCap
hasConsecutiveCAPS
NewNASAModule

Выход:

Word 1 of 4 = "one"
Word 2 of 4 = "Two"
Word 3 of 4 = "Three"
Word 4 of 4 = "Four"

Word 1 of 3 = "Starts"
Word 2 of 3 = "With"
Word 3 of 3 = "Cap"

Word 1 of 3 = "has"
Word 2 of 3 = "Consecutive"
Word 3 of 3 = "CAPS"

Word 1 of 3 = "New"
Word 2 of 3 = "NASA"
Word 3 of 3 = "Module"

Отредактировано: 2014-04-12: Изменено регулярное выражение, сценарий и данные теста для правильного разделения: "NewNASAModule" case (в ответ на комментарий rr).

12 голосов
/ 15 июня 2013

Функциональная версия ответа @ ridgerunner.

/**
 * Converts camelCase string to have spaces between each.
 * @param $camelCaseString
 * @return string
 */
function fromCamelCase($camelCaseString) {
        $re = '/(?<=[a-z])(?=[A-Z])/x';
        $a = preg_split($re, $camelCaseString);
        return join($a, " " );
}
11 голосов
/ 12 апреля 2014

Хотя ответ Ridgerunner работает отлично, похоже, он не работает с подстроками с заглавными буквами, которые появляются в середине предложения. Я использую следующее, и, похоже, с этим все в порядке:

function splitCamelCase($input)
{
    return preg_split(
        '/(^[^A-Z]+|[A-Z][^A-Z]+)/',
        $input,
        -1, /* no limit for replacement count */
        PREG_SPLIT_NO_EMPTY /*don't return empty elements*/
            | PREG_SPLIT_DELIM_CAPTURE /*don't strip anything from output array*/
    );
}

Некоторые тестовые случаи:

assert(splitCamelCase('lowHigh') == ['low', 'High']);
assert(splitCamelCase('WarriorPrincess') == ['Warrior', 'Princess']);
assert(splitCamelCase('SupportSEELE') == ['Support', 'SEELE']);
assert(splitCamelCase('LaunchFLEIAModule') == ['Launch', 'FLEIA', 'Module']);
assert(splitCamelCase('anotherNASATrip') == ['another', 'NASA', 'Trip']);
6 голосов
/ 02 февраля 2012
$string = preg_replace( '/([a-z0-9])([A-Z])/', "$1 $2", $string );

Хитрость - это повторяющийся паттерн $ 1 $ 2 $ 1 $ 2 или ниже UPPERlower UPPERlower и т. Д .... например helloWorld = $ 1 соответствует «hello», $ 2 соответствует «W» и $ 1 соответствует «orld», так что вкратце вы получаете $ 1 $ 2 $ 1 или «Hello World», соответствует HelloWorld как $ 2 $ 1 $ 2 $ 1 или снова «Hello World». Затем вы можете использовать строчные буквы в верхнем регистре первого слова или разбить их на пробел, или использовать _ или какой-либо другой символ, чтобы разделить их.

Коротко и просто.

2 голосов
/ 31 августа 2016

Я взял код классного парня Риджеруннера (см. Выше) и превратил его в функцию:

echo deliciousCamelcase('NewNASAModule');

function deliciousCamelcase($str)
{
    $formattedStr = '';
    $re = '/
          (?<=[a-z])
          (?=[A-Z])
        | (?<=[A-Z])
          (?=[A-Z][a-z])
        /x';
    $a = preg_split($re, $str);
    $formattedStr = implode(' ', $a);
    return $formattedStr;
}

Это вернет: New NASA Module

1 голос
/ 15 апреля 2019

При определении наилучшего шаблона для вашего проекта вам необходимо учитывать следующие факторы шаблона:

  1. Точность (робастность) - является ли шаблон корректным во всех случаях и является ли он разумным в будущем.доказательство
  2. Эффективность - шаблон должен быть прямым, преднамеренным и избегать ненужной работы
  3. Краткость - шаблон должен использовать соответствующие методы, чтобы избежать ненужной длины символов
  4. Читабельность -- шаблон должен быть максимально простым

Вышеперечисленные факторы также оказываются в иерархическом порядке, который стремиться подчиняться.Другими словами, для меня не имеет большого смысла расставлять приоритеты 2, 3 или 4, когда 1 не совсем удовлетворяет требованиям.Для меня читаемость находится внизу списка, потому что в большинстве случаев я могу следовать синтаксису.

Группы захвата и Lookarounds часто влияют на эффективность шаблона.Правда в том, что если вы не выполняете это регулярное выражение для тысяч входных строк, вам не нужно трудиться над эффективностью.Возможно, более важно сосредоточиться на удобочитаемости шаблона, который может быть связан с краткостью шаблона.

Некоторые шаблоны ниже потребуют некоторой дополнительной обработки / пометки с помощью их функции preg_, но вот некоторые сравнения шаблонов, основанные наПример ввода OP:

preg_split() шаблоны:

  • /^[^A-Z]+\K|[A-Z][^A-Z]+\K/ (21 шаг)
  • /(^[^A-Z]+|[A-Z][^A-Z]+)/ (26 шагов)
  • /[^A-Z]+\K(?=[A-Z])/ (43 шага)
  • /(?=[A-Z])/ (50 шагов)
  • /(?=[A-Z]+)/ (50 шагов)
  • /([a-z]{1})[A-Z]{1}/ (53 шага)
  • /([a-z0-9])([A-Z])/ (68 шагов)
  • /(?<=[a-z])(?=[A-Z])/x (94 шага) ... для записи, x бесполезен.
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/ (134 шага)

preg_match_all() шаблоны:

  • /[A-Z]?[a-z]+/ (14 шагов)
  • /((?:^|[A-Z])[a-z]+)/ (35 шагов)

Я укажу, что есть небольшая разница между выходными значениями preg_match_all() и preg_split().preg_match_all() выведет 2-мерный массив, другими словами, все совпадения полной строки будут находиться в подмассиве [0];если используется группа захвата, эти подстроки будут находиться в подмассиве [1].С другой стороны, preg_split() выводит только одномерный массив и, следовательно, обеспечивает менее раздутый и более прямой путь к желаемому выводу.

Некоторые шаблоны недостаточны при работе со строками camelCase, которые содержатALLCAPS / аббревиатура в них.Если это ограниченный случай, который возможен в вашем проекте, логично рассматривать только шаблоны, которые правильно обрабатывают эти случаи.Я не буду тестировать входные строки TitleCase, потому что это слишком далеко от вопроса.

Новая расширенная батарея тестовых строк:

oneTwoThreeFour
hasConsecutiveCAPS
newNASAModule
USAIsGreatAgain 

Подходит preg_split()выкройки:

  • /[a-z]+\K|(?=[A-Z][a-z]+)/ (149 шагов) * Мне пришлось использовать [a-z], чтобы демо правильно посчитало
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/ (547 шагов)

Подходящий preg_match_all() шаблон:

  • /[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|$)/ (75 шагов)

Наконец, мои рекомендации основаны на моих принципах шаблонов / иерархии факторов.Кроме того, я рекомендую preg_split() более preg_match_all() (несмотря на то, что шаблоны имеют меньше шагов) в качестве непосредственного элемента к желаемой структуре вывода.(конечно, выбирайте что хотите)

Код: ( Демо )

$noAcronyms = 'oneTwoThreeFour';
var_export(preg_split('~^[^A-Z]+\K|[A-Z][^A-Z]+\K~', $noAcronyms, 0, PREG_SPLIT_NO_EMPTY));
echo "\n---\n";
var_export(preg_match_all('~[A-Z]?[^A-Z]+~', $noAcronyms, $out) ? $out[0] : []);

Код: ( Демо )

$withAcronyms = 'newNASAModule';
var_export(preg_split('~[^A-Z]+\K|(?=[A-Z][^A-Z]+)~', $withAcronyms, 0, PREG_SPLIT_NO_EMPTY));
echo "\n---\n";
var_export(preg_match_all('~[A-Z]?[^A-Z]+|[A-Z]+(?=[A-Z][^A-Z]|$)~', $withAcronyms, $out) ? $out[0] : []);
1 голос
/ 23 декабря 2010

Другой вариант соответствует /[A-Z]?[a-z]+/ - если вы знаете, что ваш ввод в правильном формате, он должен работать хорошо.

[A-Z]? будет соответствовать заглавной букве (или ничего).[a-z]+ будет соответствовать всем следующим строчным буквам до следующего соответствия.

Рабочий пример: https://regex101.com/r/kNZfEI/1

0 голосов
/ 06 октября 2018

Полная функция на основе ответа @codaddict:

function splitCamelCase($str) {
    $splitCamelArray = preg_split('/(?=[A-Z])/', $str);

    return ucwords(implode($splitCamelArray, ' '));
}
...