Se eu tivesse:
$string = "PascalCase";
eu preciso
"pascal_case"
O PHP oferece uma função para esse propósito?
Se eu tivesse:
$string = "PascalCase";
eu preciso
"pascal_case"
O PHP oferece uma função para esse propósito?
Respostas:
Experimente para ver o tamanho:
$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);
}
Resultado:
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
Isso implementa as seguintes regras:
Uma solução mais curta: semelhante à do editor com uma expressão regular simplificada e corrigindo o problema do "sublinhado à direita":
$output = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $input));
Observe que casos como SimpleXML
serão convertidos para simple_x_m_l
usar a solução acima. Isso também pode ser considerado um uso incorreto da notação camel case (correto seria SimpleXml
) ao invés de um bug do algoritmo, uma vez que tais casos são sempre ambíguos - mesmo agrupando caracteres maiúsculos em uma string ( simple_xml
) tal algoritmo sempre falhará em outros casos extremos como XMLHTMLConverter
ou palavras de uma letra perto de abreviações, etc. Se você não se importa com os casos extremos (bastante raros) e deseja manipular SimpleXML
corretamente, você pode usar uma solução um pouco mais complexa:
$output = ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $input)), '_');
Uma solução concisa e pode lidar com alguns casos de uso complicados:
function decamelize($string) {
return strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $string));
}
Pode lidar com todos esses casos:
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
Você pode testar essa função aqui: http://syframework.alwaysdata.net/decamelize
Transferido do String#camelize
e do Ruby 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);
}
Um truque que as soluções acima podem ter falhado é o modificador 'e' que faz preg_replace
com que a string de substituição seja avaliada como código PHP.
e
flag para preg_replace
está sendo descontinuada no PHP 5.5.
^|
ou strlen
.
O Symfony Serializer Component possui um CamelCaseToSnakeCaseNameConverter que possui dois métodos normalize()
e denormalize()
. Eles podem ser usados da seguinte forma:
$nameConverter = new CamelCaseToSnakeCaseNameConverter();
echo $nameConverter->normalize('camelCase');
// outputs: camel_case
echo $nameConverter->denormalize('snake_case');
// outputs: snakeCase
$nameConverter->normalize('CamelCase')
saídas _camel_case
na versão 3.2 atual do Symfony Serializer Component.
A maioria das soluções aqui parecem opressivas. Aqui está o que eu uso:
$underscored = strtolower(
preg_replace(
["/([A-Z]+)/", "/_([A-Z]+)([A-Z][a-z])/"],
["_$1", "_$1_$2"],
lcfirst($camelCase)
)
);
"CamelCASE" é convertido em "camel_case"
lcfirst($camelCase)
irá diminuir o primeiro caractere (evita que a saída convertida de 'CamelCASE' comece com um sublinhado)[A-Z]
encontra letras maiúsculas+
tratará todas as maiúsculas consecutivas como uma palavra (evita que 'CamelCASE' seja convertido para camel_C_A_S_E)ThoseSPECCases
-> em those_spec_cases
vez dethose_speccases
strtolower([…])
transforma a saída em minúsculaslcfirst
função a $ camelCase
ucfirst()
chamada. USADollarSymbol
torna-se u_sa_dollar_symbol
Demo . Não recomendo esta solução porque ela tem que fazer duas passagens pela string de entrada com regex - um sinal de um padrão não refinado.
php não oferece uma função embutida para este afaik, mas aqui está o que eu uso
function uncamelize($camel,$splitter="_") {
$camel=preg_replace('/(?!^)[[:upper:]][[:lower:]]/', '$0', preg_replace('/(?!^)[[:upper:]]+/', $splitter.'$0', $camel));
return strtolower($camel);
}
o divisor pode ser especificado na chamada de função, então você pode chamá-lo assim
$camelized="thisStringIsCamelized";
echo uncamelize($camelized,"_");
//echoes "this_string_is_camelized"
echo uncamelize($camelized,"-");
//echoes "this-string-is-camelized"
mb_strtolower
e a /u
opção ativada preg_replace
.
Você precisa executar uma regex que corresponda a todas as letras maiúsculas, exceto se estiver no início e substituí-la por sublinhado mais essa letra. Uma solução utf-8 é esta:
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_áááá_őőőő
Se você não tiver certeza de qual é o caso de sua string, é melhor verificar primeiro, porque este código assume que a entrada é em camelCase
vez de underscore_Case
ou dash-Case
, então se os últimos tiverem letras maiúsculas, ele adicionará sublinhados a eles.
A resposta aceita de cletus é imho muito complicada e funciona apenas com caracteres latinos. Acho que é uma solução realmente ruim e me pergunto por que foi aceita. A conversão TEST123String
em test123_string
não é necessariamente um requisito válido. Prefiro mantê-lo simples e separado ABCccc
em em a_b_cccc
vez de ab_cccc
porque ele não perde informações dessa forma e a conversão para trás dará exatamente a mesma string com que começamos. Mesmo se você quiser fazer isso de outra maneira, é relativamente fácil escrever um regex para ele com lookbehind positivo (?<!^)\p{Lu}\p{Ll}|(?<=\p{Ll})\p{Lu}
ou dois regexes sem lookbehind se você não for um especialista em regex. Não há necessidade de dividi-lo em substrings, para não mencionar decidir entre strtolower
e lcfirst
onde usar strtolower
seria totalmente adequado.
Se você está procurando uma versão do PHP 5.4 e uma resposta posterior, aqui está o código:
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
);
}
Nada sofisticado, mas simples e rápido como o inferno:
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
++$i
em vez de $i++
torná-lo um pouco mais rápido também;)
"CamelCase" para "camel_case":
function camelToSnake($camel)
{
$snake = preg_replace('/[A-Z]/', '_$0', $camel);
$snake = strtolower($snake);
$snake = ltrim($snake, '_');
return $snake;
}
ou:
function camelToSnake($camel)
{
$snake = preg_replace_callback('/[A-Z]/', function ($match){
return '_' . strtolower($match[0]);
}, $camel);
return ltrim($snake, '_');
}
this-kind-of-output
Uma versão que não usa regex pode ser encontrada na fonte do 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);
}
Então aqui está uma linha:
strtolower(preg_replace('/(?|([a-z\d])([A-Z])|([^\^])([A-Z][a-z]))/', '$1_$2', $string));
g
modificador a este regex.
g
ele e funciona bem para mim.
g
. Mas não consigo me lembrar da frase com a qual testei.
danielstjules / Stringy fornece um método para converter string de camelcase em snakecase.
s('TestUCase')->underscored(); // 'test_u_case'
O Laravel 5.6 oferece uma maneira muito simples de fazer isso:
/**
* 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;
}
O que ele faz: se ele vê que há pelo menos uma letra maiúscula na string dada, ele usa um lookahead positivo para procurar qualquer caractere ( .
) seguido por uma letra maiúscula ( (?=[A-Z])
). Em seguida, ele substitui o caractere encontrado por seu valor seguido pelo separador _
.
A porta direta dos trilhos (sem o tratamento especial para :: ou acrônimos) seria
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, '-', '_'));
}
Conhecendo o PHP, isso será mais rápido do que a análise manual que está acontecendo em outras respostas fornecidas aqui. A desvantagem é que você não escolhe o que usar como separador entre as palavras, mas isso não fazia parte da pergunta.
Verifique também o código-fonte relevante do Rails
Observe que isso se destina ao uso com identificadores ASCII. Se você precisar fazer isso com caracteres fora do intervalo ASCII, use o modificador '/ u' para preg_match
e use mb_strtolower
.
Aqui está minha contribuição para uma pergunta de seis anos com sabe Deus quantas respostas ...
Ele irá converter todas as palavras na string fornecida que estão em caixa de camelo em caixa de serpente. Por exemplo, "SuperSpecialAwesome e também FizBuzz καιΚάτιΑκόμα" serão convertidos em "super_special_awesome e também fizz_buzz και_κάτι_ακόμα".
mb_strtolower(
preg_replace_callback(
'/(?<!\b|_)\p{Lu}/u',
function ($a) {
return "_$a[0]";
},
'SuperSpecialAwesome'
)
);
Yii2 tem uma função diferente para fazer a palavra snake_case do 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));
}
Solução curta:
$subject = "PascalCase";
echo strtolower(preg_replace('/\B([A-Z])/', '_$1', $subject));
Tive um problema semelhante, mas não consegui encontrar nenhuma resposta que satisfizesse como converter CamelCase para snake_case, evitando sublinhados duplicados ou redundantes _
para nomes com sublinhados ou abreviações todas em maiúsculas.
O problema é o seguinte:
CamelCaseClass => camel_case_class
ClassName_WithUnderscores => class_name_with_underscore
FAQ => faq
A solução que escrevi é uma simples chamada de duas funções, minúsculas e pesquisar e substituir por letras maiúsculas e minúsculas consecutivas:
strtolower(preg_replace("/([a-z])([A-Z])/", "$1_$2", $name));
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}
(ou $name[$k]
), o que tornaria seu código mais longo, mas evita a grande sobrecarga de convertê-lo de e para um array.
A pior resposta aqui estava tão perto de ser a melhor (use uma estrutura). NÃO, NÃO, basta dar uma olhada no código-fonte. ver o que um framework bem estabelecido usa seria uma abordagem muito mais confiável (experimentada e testada). O framework Zend possui alguns filtros de palavras que atendem às suas necessidades. Fonte .
aqui estão alguns métodos que adaptei da fonte.
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");
Existe uma biblioteca que fornece esta funcionalidade:
SnakeCaseFormatter::run('CamelCase'); // Output: "camel_case"
Se você usa o framework Laravel, você pode usar apenas o método snake_case () .
Esta é uma das maneiras mais curtas:
function camel_to_snake($input)
{
return strtolower(ltrim(preg_replace('/([A-Z])/', '_\\1', $input), '_'));
}
Como descamelizar sem usar regex:
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;
}
Uma edição:
Como eu faria isso em 2019:
function toSnakeCase($str, $glue = '_') {
return preg_replace_callback('/[A-Z]/', function ($matches) use ($glue) {
return $glue . strtolower($matches[0]);
}, $str);
}
E quando o PHP 7.4 será lançado:
function toSnakeCase($str, $glue = '_') {
return preg_replace_callback('/[A-Z]/', fn($matches) => $glue . strtolower($matches[0]), $str);
}
É fácil usar as classes Filter dos Filtros do Zend Word :
<?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
fácil >>> fácil
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
fácil >>> fácil
HTML >>> html
simpleXML >>> simple_xml
PDFLoad >>> pdf_load
startMIDDLELast >>> start_middle_last
AString >>> a_string
Some4Numbers234 >>> some4_numbers234
TEST123String >>> test123_string
A biblioteca de código aberto TurboCommons contém um método formatCase () de propósito geral dentro da classe StringUtils, que permite converter uma string em vários formatos de caso comuns, como CamelCase, UpperCamelCase, LowerCamelCase, snake_case, Title Case e muitos mais.
https://github.com/edertone/TurboCommons
Para usá-lo, importe o arquivo phar para o seu projeto e:
use org\turbocommons\src\main\php\utils\StringUtils;
echo StringUtils::formatCase('camelCase', StringUtils::FORMAT_SNAKE_CASE);
// will output 'camel_Case'
$str = 'FooBarBaz';
return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $str)); // foo_bar_baz
SE você pudesse começar com:
$string = 'Camel_Case'; // underscore or any other separator...
Então você pode converter para qualquer caso apenas com:
$pascal = str_replace("_", "", $string);
$snake = strtolower($string);
Ou qualquer outro caso:
$capitalized = str_replace("_", " ", $string); // Camel Case
$constant = strtoupper($string); // CAMEL_CASE
$train = str_replace("_", "-", $snake); // camel-case