Pergunta

Se eu tivesse:

$string = "PascalCase";

Eu preciso de

"pascal_case"

O PHP oferece uma função para esse fim?

Foi útil?

Solução

Experimente 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:

  1. Uma sequência que inicia com uma letra minúscula deve ser seguida por letras e dígitos minúsculos;
  2. Uma sequência começando com uma letra de maiúsculas pode ser seguida por:
    • uma ou mais letras e dígitos maiúsculos (seguidos pelo final da corda ou uma letra de maiúsculas, seguida por uma letra minúscula ou dígito, ou seja, o início da próxima sequência); ou
    • uma ou mais letras ou dígitos minúsculos.

Outras dicas

Uma solução mais curta: semelhante ao Editor um com uma expressão regular simplificada e corrigindo o problema "à direita":

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

Demo PHP | Demoção regex


Observe que casos como SimpleXML será convertido para simple_x_m_l usando a solução acima. Isso também pode ser considerado um uso errado da notação de casos de camelo (correto seria SimpleXml) em vez de um bug do algoritmo, pois esses casos são sempre ambíguos - mesmo agrupando caracteres maiúsculos em uma string (simple_xml) esse algoritmo sempre falhará em outros casos de borda, como XMLHTMLConverter ou palavras de uma letra perto de abreviações, etc. Se você não se importa com os casos de borda (bastante raros) e deseja lidar 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)), '_');

Demo PHP | Demoção regex

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 esta função aqui: http://syframework.alwaysdata.net/decamelize

Portado do Ruby's String#camelize e 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 perdido é o modificador 'e' que causa preg_replace Para avaliar a sequência de substituição como código PHP.

O Componente serializador Symfony tem um CamelCaseToSnakeCaseNameConverter que tem 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

A maioria das soluções aqui parece pesada. 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 para "camel_case"

  • lcfirst($camelCase) Abaixará o primeiro caractere (evita a saída convertida 'CamelCase' para iniciar com um sublinhado)
  • [A-Z] encontra letras maiúsculas
  • + tratará todas as maiúsculas consecutivas como uma palavra (evita 'camelCase' a ser convertido em camel_c_a_s_e)
  • Segundo padrão e substituição são para ThoseSPECCases -> those_spec_cases ao invés de those_speccases
  • strtolower([…]) gira a saída para as casas inferiores

O 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, para que você possa chamá -lo como assim

$camelized="thisStringIsCamelized";
echo uncamelize($camelized,"_");
//echoes "this_string_is_camelized"
echo uncamelize($camelized,"-");
//echoes "this-string-is-camelized"
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ê está procurando uma versão Php 5.4 e posteriormente 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
    );

} 

Não é de nada, 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

"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, '_');
}

Uma versão que não usa regex pode ser encontrada no Alquiteto fonte:

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));

Danielstjules/Salty provou um método para converter string de camelcase em snakecase.

s('TestUCase')->underscored(); // 'test_u_case'

O Laravel 5.6 fornece 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 faz: se vê que há pelo menos uma letra maiúscula na string dada, ele usa um Lookahead positivo Para procurar qualquer personagem (.) seguido por uma letra maiúscula ((?=[A-Z])). Em seguida, substitui o personagem encontrado por seu valor seguido pelo separattor _.

O porto direto da Rails (menos seu manuseio 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 que a análise manual que está acontecendo em outras respostas dadas 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 dos Rails

Observe que isso se destina ao uso com identificadores ASCII. Se você precisar fazer isso com caracteres fora da linha ASCII, use o modificador '/u' para preg_matchE use mb_strtolower.

Aqui está minha contribuição para uma pergunta de seis anos com Deus sabe quantas respostas ...

Ele converterá todas as palavras na string fornecida que estão no camelcase em snakecase. Por exemplo, "Superspecialawesome e também Fizbuzz καικάτιακόμα" será convertido em "super_special_awesome e também fezz_buzz και_κάτι_ακrig".

mb_strtolower(
    preg_replace_callback(
        '/(?<!\b|_)\p{Lu}/u',
        function ($a) {
            return "_$a[0]";
        },
        'SuperSpecialAwesome'
    )
);

Yii2 tem a função diferente para fazer a palavra Snake_Case a partir da 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));
    }
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);
}

Existe um biblioteca Fornecendo essa funcionalidade:

SnakeCaseFormatter::run('CamelCase'); // Output: "camel_case"

Se você usar a estrutura do Laravel, você pode usar apenas Snake_Case () método.

$str = 'FooBarBaz';

return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $str)); // foo_bar_baz

Esta é uma das maneiras mais curtas:

function camel_to_snake($input)
{
    return strtolower(ltrim(preg_replace('/([A-Z])/', '_\\1', $input), '_'));
}

Como descamelizar sem usar o 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);
}

Solução curta:

$subject = "PascalCase";
echo strtolower(preg_replace('/\B([A-Z])/', '_$1', $subject));

É fácil usar as classes de filtro do Zend Filtros de palavras:

<?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 >>> Atring

Some4_numbers234 >>> Some4Numbers234

TEST123_STRING >>> TEST123STRING

----- CamelCasetounderScore -----

SimpleTest >>> Simple_test

fácil >>> fácil

Html >>> html

SimpXML >>> simples_xml

Pdfload >>> pdf_load

startMiddleLast >>> start_middle_last

Astring >>> a_string

Some4Numbers234 >>> Some4_numbers234

TEST123STRING >>> TEST123_STRING

A pior resposta aqui foi tão próxima de ser a melhor (use uma estrutura). Não, não, basta dar uma olhada no código -fonte. Ver o que uma estrutura bem estabelecida usa seria uma abordagem muito mais confiável (testada e testada). A estrutura Zend possui alguns filtros de palavras que atendem às suas necessidades. Fonte.

Aqui estão alguns métodos que me 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");

A Biblioteca de Turbocommons de código aberto contém um método de formatCase () de propósito geral dentro da classe Stringutils, que permite converter uma string em muitos formatos de caixa comum, como camelcase, uppercamelcase, LowerCamelcase, Snake_Case, título e muito 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'

Eu tive um problema semelhante, mas não consegui encontrar nenhuma resposta que satisfaça como converter o camelcase em Snake_case, evitando sublinhados duplicados ou redundantes _ Para nomes com sublinhados ou abreviações de todas as caps.

O problema é como seguir:

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

A solução que escrevi é uma chamada simples de duas funções, minúsculas e pesquisar e substituir por letras consecutivas de minúsculas de menor suporte:

strtolower(preg_replace("/([a-z])([A-Z])/", "$1_$2", $name));

Se você pudesse começar com:

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

Então você pode se converter para qualquer um dos casos apenas com:

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

Ou quaisquer outros casos:

$capitalized = str_replace("_", " ", $string); // Camel Case
$constant = strtoupper($string);               // CAMEL_CASE
$train = str_replace("_", "-", $snake);        // camel-case
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top