Как преобразовать PascalCase в pascal_case?

115

Если бы у меня было:

$string = "PascalCase";

я нуждаюсь

"pascal_case"

Предлагает ли PHP функцию для этой цели?

openfrog
источник
31
Технически первая строка примера - это PascalCase.
Робин ван Баален,
33
И вторая строка примера известна как snake_case .
Панг

Ответы:

163

Примерьте это на размер:

$tests = array(
  'simpleTest' => 'simple_test',
  'easy' => 'easy',
  'HTML' => 'html',
  'simpleXML' => 'simple_xml',
  'PDFLoad' => 'pdf_load',
  'startMIDDLELast' => 'start_middle_last',
  'AString' => 'a_string',
  'Some4Numbers234' => 'some4_numbers234',
  'TEST123String' => 'test123_string',
);

foreach ($tests as $test => $result) {
  $output = from_camel_case($test);
  if ($output === $result) {
    echo "Pass: $test => $result\n";
  } else {
    echo "Fail: $test => $result [$output]\n";
  }
}

function from_camel_case($input) {
  preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
  $ret = $matches[0];
  foreach ($ret as &$match) {
    $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
  }
  return implode('_', $ret);
}

Вывод:

Pass: simpleTest => simple_test
Pass: easy => easy
Pass: HTML => html
Pass: simpleXML => simple_xml
Pass: PDFLoad => pdf_load
Pass: startMIDDLELast => start_middle_last
Pass: AString => a_string
Pass: Some4Numbers234 => some4_numbers234
Pass: TEST123String => test123_string

Это реализует следующие правила:

  1. Последовательность, начинающаяся со строчной буквы, должна сопровождаться строчными буквами и цифрами;
  2. За последовательностью, начинающейся с прописной буквы, может следовать:
    • одна или несколько прописных букв и цифр (за которыми следует либо конец строки, либо прописная буква, за которой следует строчная буква или цифра, т.е. начало следующей последовательности); или
    • одна или несколько строчных букв или цифр.
Клетус
источник
9
Он работает для строк CamelCased (как просил openfrog), но если вы используете его со строкой ввода, например, «r_id» (уже «подчеркнутый»), он обрезает префикс («r_»). Хорошее решение, но однозначно не универсальное.
Мартин
1
Любопытно, почему вы проверяете, соответствует ли строка строке с заглавными буквами? В чем преимущество преобразования только первого символа в нижний регистр (в отличие от всех символов)?
Джош
1
Более краткое решение, которое также может обрабатывать все эти варианты использования: stackoverflow.com/a/35719689/4328383
Syone 01
156

Более короткое решение: аналогично редактору с упрощенным регулярным выражением и исправлением проблемы "подчеркивания в конце":

$output = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $input));

Демо PHP | Демо Regex


Обратите внимание, что такие случаи SimpleXMLбудут преобразованы в simple_x_m_lиспользование вышеуказанного решения. Это также можно считать неправильным использованием обозначения верблюжьего регистра (правильным было бы SimpleXml), а не ошибкой алгоритма, поскольку такие случаи всегда неоднозначны - даже путем группировки символов верхнего регистра в одну строку ( simple_xml) такой алгоритм всегда будет терпеть неудачу в других крайних случаях например, XMLHTMLConverterили однобуквенные слова рядом с сокращениями и т. д. Если вы не возражаете против (довольно редких) крайних случаев и хотите обрабатывать SimpleXMLправильно, вы можете использовать немного более сложное решение:

$output = ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $input)), '_');

Демо PHP | Демо Regex

Ян Якеш
источник
Не стесняйтесь комментировать ответ cletus, подробно описывая, какие тестовые случаи вы исправили.
Майк Би
3
Я не говорю, что его решение дает неправильные результаты. Его решение просто крайне сложное и малоэффективное.
Ян Якеш
1
Да, ответ "принять" определенно провален. Решение Яна потрясающее! В качестве побочного примечания, я думаю, что это (или небольшая вариация) мой новый любимый тест кодирования для разработчиков PHP, потому что количество ответов на этот вопрос, которые на самом деле не работают, невероятно. Это был бы отличный способ выполнить первоначальную фильтрацию. :-)
JamesG 05
обнаружил, что регулярное выражение, используемое в этом решении, намного более полно: stackoverflow.com/questions/2559759/…
Thoroc
2
Хорошее решение для простых случаев использования, и в большинстве обычных случаев этого достаточно, но принятое решение может обрабатывать больше вариантов использования, например, «simpleXML» может быть преобразован в «simple_xml», а не в «simple_x_m_l»
Syone 01
35

Краткое решение, способное справиться с некоторыми сложными случаями использования:

function decamelize($string) {
    return strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $string));
}

Может справиться со всеми этими случаями:

simpleTest => simple_test
easy => easy
HTML => html
simpleXML => simple_xml
PDFLoad => pdf_load
startMIDDLELast => start_middle_last
AString => a_string
Some4Numbers234 => some4_numbers234
TEST123String => test123_string
hello_world => hello_world
hello__world => hello__world
_hello_world_ => _hello_world_
hello_World => hello_world
HelloWorld => hello_world
helloWorldFoo => hello_world_foo
hello-world => hello-world
myHTMLFiLe => my_html_fi_le
aBaBaB => a_ba_ba_b
BaBaBa => ba_ba_ba
libC => lib_c

Вы можете протестировать эту функцию здесь: http://syframework.alwaysdata.net/decamelize

Syone
источник
@VivekVardhan, какую часть этого регулярного выражения вы не понимаете?
Syone
Гм, я думаю, что строчные строки без верблюжьего регистра - это побочный эффект, если строка не в формате верблюжьего регистра, должна быть возвращена исходная. Фактически, если вы отправите 'simple_Text', вы получите Fail: simple_Test => simple_Test [simple_test]. Строка в нижнем регистре должна быть сделана только и только в том случае, если исходная строка является настоящей строкой в ​​верблюжьем регистре. О чем вы думаете?
guido
24

Портировано из Ruby's String#camelizeи String#decamelize.

function decamelize($word) {
  return preg_replace(
    '/(^|[a-z])([A-Z])/e', 
    'strtolower(strlen("\\1") ? "\\1_\\2" : "\\2")',
    $word 
  ); 
}

function camelize($word) { 
  return preg_replace('/(^|_)([a-z])/e', 'strtoupper("\\2")', $word); 
}

Одна уловка, которую могли упустить вышеупомянутые решения, - это модификатор 'e', ​​который заставляет preg_replaceоценивать строку замены как код PHP.

user644783
источник
10
eФлаг preg_replaceявляется устаревшим в PHP 5.5.
cdmckay
Кстати, их тоже нет в Ruby, а в библиотеке инфлекторов Rails - camelize и подчеркивание. api.rubyonrails.org/classes/ActiveSupport/Inflector.html
mahemoff 05
2
Это не для "ThisIsATest". Кажется, что не поддерживает два последовательных заглавных регистра.
OnaBai
Просто примечание: вы можете использовать lcfirst, чтобы перевести первую букву в нижний регистр, тогда вам не нужно ^|или strlen.
Benubird
декамелизовать без устаревания: gist.github.com/scones/e09c30e696246fda14578bcf8ab4910a
scones
23

Symfony Serializer имеет CamelCaseToSnakeCaseNameConverter, который имеет два метода normalize()и denormalize(). Их можно использовать следующим образом:

$nameConverter = new CamelCaseToSnakeCaseNameConverter();

echo $nameConverter->normalize('camelCase');
// outputs: camel_case

echo $nameConverter->denormalize('snake_case');
// outputs: snakeCase
Мэтью
источник
1
Осторожно! $nameConverter->normalize('CamelCase')выводит _camel_caseв текущей версии 3.2 компонента Symfony Serializer.
spackmat
21

Большинство решений здесь кажутся тяжелыми. Вот что я использую:

$underscored = strtolower(
    preg_replace(
        ["/([A-Z]+)/", "/_([A-Z]+)([A-Z][a-z])/"], 
        ["_$1", "_$1_$2"], 
        lcfirst($camelCase)
    )
);

«CamelCASE» преобразуется в «camel_case»

  • lcfirst($camelCase) понизит первый символ (избегает преобразованного вывода 'CamelCASE', чтобы он начинался с подчеркивания)
  • [A-Z] находит заглавные буквы
  • + будет обрабатывать каждый последующий верхний регистр как слово (избегает преобразования CamelCASE в camel_C_A_S_E)
  • Второй шаблон и замена для ThoseSPECCases-> those_spec_casesвместоthose_speccases
  • strtolower([…]) переводит вывод в нижний регистр
buley
источник
3
Но он также превращает CamelCased в _camel_cased.
acme
1
это здорово - просто добавьте подстановку, начинающуюся с символа 1, чтобы обойти эту проблему.
Oddman 09
4
Отлично! Просто нужно добавить lcfirstфункцию в $ camelCase
Эдакос
Принятый ответ будет обрабатывать: TestUPSClass в test_ups_class, а это превратит его в test_u_p_s_class, о чем следует помнить.
Mazzy
Строка ввода, которая начинается с первого «слова» allcaps, будет неожиданно разделена этим решением из-за ucfirst()вызова. USADollarSymbolстановится u_sa_dollar_symbol Demo. Я не рекомендую это решение, потому что оно должно выполнить два прохода через входную строку с регулярным выражением - признак неочищенного шаблона.
mickmackusa
19

php не предлагает встроенной функции для этого afaik, но вот что я использую

function uncamelize($camel,$splitter="_") {
    $camel=preg_replace('/(?!^)[[:upper:]][[:lower:]]/', '$0', preg_replace('/(?!^)[[:upper:]]+/', $splitter.'$0', $camel));
    return strtolower($camel);

}

разделитель может быть указан в вызове функции, поэтому вы можете называть его так

$camelized="thisStringIsCamelized";
echo uncamelize($camelized,"_");
//echoes "this_string_is_camelized"
echo uncamelize($camelized,"-");
//echoes "this-string-is-camelized"
ekhaled
источник
2
Это не для "ThisIsATest". Кажется, что не поддерживает два последовательных заглавных регистра.
OnaBai
Наверняка вы что-то забыли, так как вторая замена ничего не делает. Кроме того, вы можете легко сделать его совместимым с Unicode mb_strtolowerи /uвключить опцию preg_replace.
bodo
8

Вам нужно запустить через него регулярное выражение, которое соответствует каждой заглавной букве, за исключением того, если оно находится в начале, и заменить его подчеркиванием плюс эта буква. Решение utf-8 таково:

header('content-type: text/html; charset=utf-8');
$separated = preg_replace('%(?<!^)\p{Lu}%usD', '_$0', 'AaaaBbbbCcccDdddÁáááŐőőő');
$lower = mb_strtolower($separated, 'utf-8');
echo $lower; //aaaa_bbbb_cccc_dddd_áááá_őőőő

Если вы не уверены, в каком регистре ваша строка, лучше сначала ее проверить, потому что этот код предполагает, что ввод camelCaseвместо underscore_Caseилиdash-Case , поэтому, если последние имеют прописные буквы, он добавит к ним подчеркивания.

Принятый ответ от cletus слишком сложен imho и работает только с латинскими символами. Я считаю это действительно плохим решением и задаюсь вопросом, почему оно вообще было принято. Преобразование TEST123Stringв test123_stringне обязательно является допустимым требованием. Я предпочел сохранить его простым и разделенным ABCcccна a_b_ccccвместо этого, ab_ccccпотому что он не теряет информацию таким образом, и обратное преобразование даст ту же самую строку, с которой мы начали. Даже если вы хотите сделать это по-другому, относительно легко написать для него регулярное выражение с положительным просмотром назад (?<!^)\p{Lu}\p{Ll}|(?<=\p{Ll})\p{Lu}или два регулярных выражения без просмотра назад, если вы не являетесь экспертом по регулярным выражениям. Нет необходимости разбивать его на подстроки, не говоря уже о выборе между strtolowerи lcfirstгде использование strtolowerбыло бы совершенно нормально.

inf3rno
источник
Ответы только на коде не имеют большого значения для Stackoverflow, потому что они мало что делают для обучения / расширения возможностей тысяч будущих исследователей.
mickmackusa
@mickmackusa Если исследователи научатся программировать из SO, тогда у нас
возникнет
Теперь, когда вы исключили эту личную атаку из вашей системы, пожалуйста, улучшите свой ответ. Предполагая, что вы знаете, как работает ваше решение и почему вы используете эти модификаторы шаблона, я не вижу веских причин скрывать знания от этого сообщества. Если вы планируете оставлять более резкие ответы, уверяю вас, они меня не беспокоят. За время, которое вы потратили на комментирование, вы могли бы завершить свой ответ, мы могли бы удалить наши комментарии, и я мог бы пойти в другое место, чтобы помочь этому сайту.
mickmackusa
Конечно, у меня нет полномочий удалять пост с 8 голосами. При желании вы можете удалить свой ответ, но не составит большого труда просто улучшить его, удалив ненужные модификаторы шаблона и добавив объяснение. Личные нападки на меня не действуют.
mickmackusa
@mickmackusa Я тоже не думаю, что смогу его удалить. Не стесняйтесь редактировать его, если хотите.
inf3rno 07
6

Если вы ищете версию PHP 5.4 и более позднюю, ответьте вот на этот код:

function decamelize($word) {
      return $word = preg_replace_callback(
        "/(^|[a-z])([A-Z])/",
        function($m) { return strtolower(strlen($m[1]) ? "$m[1]_$m[2]" : "$m[2]"); },
        $word
    );

}
function camelize($word) {
    return $word = preg_replace_callback(
        "/(^|_)([a-z])/",
        function($m) { return strtoupper("$m[2]"); },
        $word
    );

} 
shacharsol
источник
camelize производит "SmsSent" для sms_sent, вам нужен lcfirst
mik3fly-4steri5k
4

Совсем не причудливо, но просто и чертовски быстро:

function uncamelize($str) 
{
    $str = lcfirst($str);
    $lc = strtolower($str);
    $result = '';
    $length = strlen($str);
    for ($i = 0; $i < $length; $i++) {
        $result .= ($str[$i] == $lc[$i] ? '' : '_') . $lc[$i];
    }
    return $result;
}

echo uncamelize('HelloAWorld'); //hello_a_world
Edakos
источник
++$iвместо того, $i++чтобы сделать его немного быстрее;)
Mathieu Amiot
Ответы только на коде не имеют большого значения для Stackoverflow, потому что они мало что делают для обучения / расширения возможностей тысяч будущих исследователей.
mickmackusa
4

«CamelCase» в «camel_case»:

function camelToSnake($camel)
{
    $snake = preg_replace('/[A-Z]/', '_$0', $camel);
    $snake = strtolower($snake);
    $snake = ltrim($snake, '_');
    return $snake;
}

или:

function camelToSnake($camel)
{
    $snake = preg_replace_callback('/[A-Z]/', function ($match){
        return '_' . strtolower($match[0]);
    }, $camel);
    return ltrim($snake, '_');
}
Xiaojing
источник
Спасибо. Я использовал первый подход, но с дефисами для генерацииthis-kind-of-output
thexpand 06
3

Версию, в которой не используется регулярное выражение, можно найти в исходном коде Alchitect :

decamelize($str, $glue='_')
{
    $counter  = 0;
    $uc_chars = '';
    $new_str  = array();
    $str_len  = strlen($str);

    for ($x=0; $x<$str_len; ++$x)
    {
        $ascii_val = ord($str[$x]);

        if ($ascii_val >= 65 && $ascii_val <= 90)
        {
            $uc_chars .= $str[$x];
        }
    }

    $tok = strtok($str, $uc_chars);

    while ($tok !== false)
    {
        $new_char  = chr(ord($uc_chars[$counter]) + 32);
        $new_str[] = $new_char . $tok;
        $tok       = strtok($uc_chars);

        ++$counter;
    }

    return implode($new_str, $glue);
}
Даррелл Брогдон
источник
2
Вот какой была бы жизнь без регулярного выражения :-)
ekhaled 03
4
Хех, да. RegEx определенно имеет свои преимущества. :) Грубая скорость не входит в их число.
Даррелл Брогдон
по какой-то причине получил забавные результаты с этим
mr1031011
У меня не работает на основе этой строки: "CamelCaseTestAAATestAA", должно быть: "camel_case_test_a_a_a_test_a_a", имеет: "" camel_case_test_aest "...
Sybio
3

Итак, вот однострочный:

strtolower(preg_replace('/(?|([a-z\d])([A-Z])|([^\^])([A-Z][a-z]))/', '$1_$2', $string));
seelts
источник
Хорошо, но он преобразует только первое появление, поэтому я бы рекомендовал добавить gмодификатор к этому регулярному выражению.
acme
@acme, пользуюсь без gи у меня работает нормально.
Seelts
По какой-то причине в моем случае мне пришлось добавить расширение g. Но я не могу вспомнить фразу, которую тестировал.
acme
3

danielstjules / Stringy предлагает метод преобразования строки из camelcase в snakecase.

s('TestUCase')->underscored(); // 'test_u_case'
Джимми Ко
источник
3

Laravel 5.6 предоставляет очень простой способ сделать это:

 /**
 * Convert a string to snake case.
 *
 * @param  string  $value
 * @param  string  $delimiter
 * @return string
 */
public static function snake($value, $delimiter = '_'): string
{
    if (!ctype_lower($value)) {
        $value = strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
    }

    return $value;
}

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

Valdrinium
источник
Эта функция теперь называется snake_case () и живет в глобальном пространстве имен.
Wotuu
2

Прямой перенос с рельсов (без особой обработки :: или акронимов) будет

function underscore($word){
    $word = preg_replace('#([A-Z\d]+)([A-Z][a-z])#','\1_\2', $word);
    $word = preg_replace('#([a-z\d])([A-Z])#', '\1_\2', $word);
    return strtolower(strtr($word, '-', '_'));
}

Зная PHP, это будет быстрее, чем ручной синтаксический анализ, который происходит в других приведенных здесь ответах. Недостатком является то, что вы не можете выбрать, что использовать в качестве разделителя между словами, но это не было частью вопроса.

Также проверьте соответствующий исходный код rails

Обратите внимание, что это предназначено для использования с идентификаторами ASCII. Если вам нужно сделать это с символами вне диапазона ASCII, используйте модификатор '/ u' для preg_matchи используйте mb_strtolower.

pilif
источник
Вы могли бы, если просто добавили параметр, содержащий желаемый символ.
Fleshgrinder
2

Вот мой вклад в вопрос шестилетней давности, на который черт знает сколько ответов ...

Он преобразует все слова в предоставленной строке, записанные в camelcase, в snakecase. Например, «SuperSpecialAwesome, а также FizBuzz καιΚάτιΑκόμα» будет преобразован в «super_special_awesome, а также fizz_buzz και_κάτι_ακόμα».

mb_strtolower(
    preg_replace_callback(
        '/(?<!\b|_)\p{Lu}/u',
        function ($a) {
            return "_$a[0]";
        },
        'SuperSpecialAwesome'
    )
);
Loupax
источник
2

Yii2 имеет другую функцию для преобразования слова snake_case из CamelCase.

    /**
     * Converts any "CamelCased" into an "underscored_word".
     * @param string $words the word(s) to underscore
     * @return string
     */
    public static function underscore($words)
    {
        return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words));
    }
Анил Чаудхари
источник
2

Краткое решение:

$subject = "PascalCase";
echo strtolower(preg_replace('/\B([A-Z])/', '_$1', $subject));
Геворг Мелкумян
источник
2

У меня была аналогичная проблема, но я не смог найти ни одного ответа, который бы удовлетворял преобразованию CamelCase в snake_case, избегая при этом повторяющихся или избыточных подчеркиваний _ для имен с подчеркиванием или аббревиатур с заглавными буквами.

Проблема заключается в следующем:

CamelCaseClass            => camel_case_class
ClassName_WithUnderscores => class_name_with_underscore
FAQ                       => faq

Решение, которое я написал, представляет собой простой вызов двух функций, нижний регистр и поиск и замену последовательных букв нижнего и верхнего регистра:

strtolower(preg_replace("/([a-z])([A-Z])/", "$1_$2", $name));
ММСС
источник
Безусловно, это наиболее краткое и полезное решение IMO.
Мистер Шань0
1
function camel2snake($name) {
    $str_arr = str_split($name);
    foreach ($str_arr as $k => &$v) {
        if (ord($v) >= 64 && ord($v) <= 90) { // A = 64; Z = 90
            $v = strtolower($v);
            $v = ($k != 0) ? '_'.$v : $v;
        }
    }
    return implode('', $str_arr);
}
Курт Чжун
источник
Вы можете получить доступ к символам напрямую, используя $name{$k}(или $name[$k]), что сделает ваш код длиннее, но позволяет избежать больших накладных расходов на преобразование его в массив и из него.
bodo 02
Ответы только на коде не имеют большого значения в StackOverflow, потому что они плохо справляются с расширением возможностей / обучением будущих исследователей. Ваше решение, избегая изящества регулярных выражений, очень тяжеловесно и запутанно. Вы разделяете каждый символ и делаете несколько итерационных вызовов функций. Нет необходимости указывать в качестве клея пустую строку. Я бы не стал использовать это решение в одном из своих проектов, потому что в нем нет элегантности, плохой читаемости и n ненужных вызовов функций.
mickmackusa
1

Худший ответ здесь был так близок к лучшему (используйте фреймворк). НЕТ, просто взгляните на исходный код. гораздо более надежный подход (опробованный и проверенный) было бы увидеть, что использует хорошо зарекомендовавшая себя структура. Во фреймворке Zend есть несколько фильтров слов, которые соответствуют вашим потребностям. Источник .

вот пара методов, которые я адаптировал из источника.

function CamelCaseToSeparator($value,$separator = ' ')
{
    if (!is_scalar($value) && !is_array($value)) {
        return $value;
    }
    if (defined('PREG_BAD_UTF8_OFFSET_ERROR') && preg_match('/\pL/u', 'a') == 1) {
        $pattern     = ['#(?<=(?:\p{Lu}))(\p{Lu}\p{Ll})#', '#(?<=(?:\p{Ll}|\p{Nd}))(\p{Lu})#'];
        $replacement = [$separator . '\1', $separator . '\1'];
    } else {
        $pattern     = ['#(?<=(?:[A-Z]))([A-Z]+)([A-Z][a-z])#', '#(?<=(?:[a-z0-9]))([A-Z])#'];
        $replacement = ['\1' . $separator . '\2', $separator . '\1'];
    }
    return preg_replace($pattern, $replacement, $value);
}
function CamelCaseToUnderscore($value){
    return CamelCaseToSeparator($value,'_');
}
function CamelCaseToDash($value){
    return CamelCaseToSeparator($value,'-');
}
$string = CamelCaseToUnderscore("CamelCase");
TarranJones
источник
1

Существует библиотека, обеспечивающая эту функциональность:

SnakeCaseFormatter::run('CamelCase'); // Output: "camel_case"
Колюня
источник
1
Я думаю, вы имеете в виду: «Я создал библиотеку, обеспечивающую эту функцию». В саморекламе нет ничего плохого, но не скрывайте этого.
icc97
1

Если вы используете фреймворк Laravel, вы можете использовать только метод snake_case () .

Марек Скиба
источник
1

Это один из более коротких способов:

function camel_to_snake($input)
{
    return strtolower(ltrim(preg_replace('/([A-Z])/', '_\\1', $input), '_'));
}
Айман Гадо
источник
Ответы только на коде не имеют большого значения для Stackoverflow, потому что они мало что делают для обучения / расширения возможностей тысяч будущих исследователей.
mickmackusa
1
@mickmackusa - тысячи будущих исследователей будут заинтересованы в элегантном остроте и самообразовываются.
Teson
Мне жаль, что вы заняли такую ​​эгоистичную позицию. Вы, конечно, могли бы добавить объяснение за то время, которое вам потребовалось, чтобы придумать и напечатать этот язвительный ответ. Ваш ответ вызывает три вызова функций, но другие выполняют задачу за два.
mickmackusa
1

Как де-камелизовать без использования регулярного выражения:

function decamelize($str, $glue = '_') {
    $capitals = [];
    $replace  = [];

    foreach(str_split($str) as $index => $char) {
        if(!ctype_upper($char)) {
            continue;
        }

        $capitals[] = $char;
        $replace[]  = ($index > 0 ? $glue : '') . strtolower($char);
    }

    if(count($capitals) > 0) {
        return str_replace($capitals, $replace, $str);
    }

    return $str;
}

Редактирование:

Как бы я это сделал в 2019 году:

function toSnakeCase($str, $glue = '_') {
    return preg_replace_callback('/[A-Z]/', function ($matches) use ($glue) {
        return $glue . strtolower($matches[0]);
    }, $str);
}

И когда выйдет PHP 7.4:

function toSnakeCase($str, $glue = '_') {
    return preg_replace_callback('/[A-Z]/', fn($matches) => $glue . strtolower($matches[0]), $str);
}
Baldrs
источник
1
Ответы только на коде не имеют большого значения в StackOverflow, потому что они плохо справляются с расширением возможностей / обучением будущих исследователей. Выполнение от 1 до 3 вызовов функций для каждого символа в строке, а затем еще двух вызовов функций после завершения цикла является очень тяжелым делом. Я бы не стал принимать решение с такой плохой экономией.
mickmackusa
Это пример того, как это можно сделать без использования регулярных выражений, а не того, как его следует использовать в производстве, поэтому я не вижу вашей точки зрения, кроме того, что вы жалуетесь на 5-летний ответ, который имеет один положительный голос и вряд ли будет замечен никаких исследователей.
baldrs
Я уделяю внимание всем сообщениям, а не только тем, за которые проголосовали, или недавним. Я не жалуюсь, я предлагаю свою критику, чтобы исследователи с меньшими знаниями могли лучше понять разницу между этим ответом и другими ответами. Вы могли бы объяснить в своем сообщении, что избежать регулярного выражения было просто академической задачей. Тем не менее, есть способы сделать этот процесс более эффективным с помощью лучших методов кодирования.
mickmackusa
0

Использовать классы фильтров Zend Word Filters легко :

<?php
namespace MyNamespace\Utility;

use Zend\Filter\Word\CamelCaseToUnderscore;
use Zend\Filter\Word\UnderscoreToCamelCase;

class String
{
    public function test()
    {
        $underscoredStrings = array(
            'simple_test',
            'easy',
            'html',
            'simple_xml',
            'pdf_load',
            'start_middle_last',
            'a_string',
            'some4_numbers234',
            'test123_string',
        );
        $camelCasedStrings = array(
            'simpleTest',
            'easy',
            'HTML',
            'simpleXML',
            'PDFLoad',
            'startMIDDLELast',
            'AString',
            'Some4Numbers234',
            'TEST123String',
        );
        echo PHP_EOL . '-----' . 'underscoreToCamelCase' . '-----' . PHP_EOL;
        foreach ($underscoredStrings as $rawString) {
            $filteredString = $this->underscoreToCamelCase($rawString);
            echo PHP_EOL . $rawString . ' >>> ' . $filteredString . PHP_EOL;
        }
        echo PHP_EOL . '-----' . 'camelCaseToUnderscore' . '-----' . PHP_EOL;
        foreach ($camelCasedStrings as $rawString) {
            $filteredString = $this->camelCaseToUnderscore($rawString);
            echo PHP_EOL . $rawString . ' >>> ' . $filteredString . PHP_EOL;
        }
    }

    public function camelCaseToUnderscore($input)
    {
        $camelCaseToSeparatorFilter = new CamelCaseToUnderscore();
        $result = $camelCaseToSeparatorFilter->filter($input);
        $result = strtolower($result);
        return $result;
    }

    public function underscoreToCamelCase($input)
    {
        $underscoreToCamelCaseFilter = new UnderscoreToCamelCase();
        $result = $underscoreToCamelCaseFilter->filter($input);
        return $result;
    }
}

----- ----- underscoreToCamelCase

simple_test >>> SimpleTest

легко >>> Легко

html >>> Html

simple_xml >>> SimpleXml

pdf_load >>> PdfLoad

start_middle_last >>> StartMiddleLast

a_string >>> AString

some4_numbers234 >>> Some4Numbers234

test123_string >>> Test123String

----- ----- camelCaseToUnderscore

simpleTest >>> simple_test

легко >>> легко

HTML >>> html

simpleXML >>> simple_xml

PDFLoad >>> pdf_load

startMIDDLELast >>> start_middle_last

AString >>> a_string

Some4Numbers234 >>> some4_numbers234

TEST123String >>> test123_string

automatix
источник
0

Библиотека TurboCommons с открытым исходным кодом содержит метод formatCase () общего назначения внутри класса StringUtils, который позволяет преобразовывать строку во множество распространенных форматов регистра, например CamelCase, UpperCamelCase, LowerCamelCase, snake_case, Title Case и многие другие.

https://github.com/edertone/TurboCommons

Чтобы использовать его, импортируйте файл phar в свой проект и:

use org\turbocommons\src\main\php\utils\StringUtils;

echo StringUtils::formatCase('camelCase', StringUtils::FORMAT_SNAKE_CASE);

// will output 'camel_Case'
Жауме Муссонс Абад
источник
0
$str = 'FooBarBaz';

return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $str)); // foo_bar_baz
Омар Маклед
источник
1
Ответы только на коде не имеют большого значения в StackOverflow, потому что они плохо справляются с расширением возможностей / обучением будущих исследователей.
mickmackusa
-1

ЕСЛИ вы могли бы начать с:

$string = 'Camel_Case'; // underscore or any other separator...

Затем вы можете преобразовать в любой из этих случаев с помощью:

$pascal = str_replace("_", "", $string);
$snake = strtolower($string);

Или любые другие случаи:

$capitalized = str_replace("_", " ", $string); // Camel Case
$constant = strtoupper($string);               // CAMEL_CASE
$train = str_replace("_", "-", $snake);        // camel-case
Нуну Рафаэль Фигейредо
источник