web-dev-qa-db-fra.com

Comment convertir CamelCase en camel_case?

Si j'avais:

$string = "CamelCase";

J'ai besoin 

"camel_case"

Est-ce que PHP offre une fonction à cette fin?

101
openfrog

Essayez ceci pour la taille:

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

Sortie:

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

Ceci implémente les règles suivantes:

  1. Une séquence commençant par une lettre minuscule doit être suivie de lettres minuscules et de chiffres;
  2. Une séquence commençant par une lettre majuscule peut être suivie de:
    • une ou plusieurs lettres majuscules et chiffres (suivis de la fin de la chaîne ou d'une lettre majuscule suivie d'une lettre minuscule ou d'un chiffre, par exemple le début de la séquence suivante); ou
    • une ou plusieurs lettres minuscules ou chiffres.
138
cletus

Une solution plus courte: Similaire à celle de editor's avec une expression rationnelle simplifiée et résolvant le problème "trailing-underscore":

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

PHP Démo | Démo Regex


Notez que les cas tels que SimpleXML seront convertis en simple_x_m_l en utilisant la solution ci-dessus. Cela peut aussi être considéré comme une mauvaise utilisation de la notation de cas de chameaux (correct serait SimpleXml) plutôt qu’un bug de l’algorithme puisque ces cas sont toujours ambigus - même en regroupant les caractères majuscules dans une chaîne (simple_xml), cet algorithme échouera toujours Les cas Edge tels que XMLHTMLConverter ou les mots d'une lettre proches des abréviations, etc. Si cela ne vous dérange pas du cas (plutôt rare) Edge et que vous souhaitez gérer SimpleXML, vous pouvez utiliser une solution un peu plus complexe:

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

PHP Démo | Démo Regex

125
Jan Jakeš

Une solution concise et peut gérer certains cas d'utilisation délicats:

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

Peut gérer tous ces cas:

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

Vous pouvez tester cette fonction ici: http://syframework.alwaysdata.net/decamelize

31
Syone

Porté à partir des String#camelize et String#decamelize de Ruby.

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

Une astuce que les solutions ci-dessus ont peut-être manquée est le modificateur 'e' qui oblige preg_replace à évaluer la chaîne de remplacement comme un code PHP.

24
user644783

La plupart des solutions ici sont lourdes. Voici ce que j'utilise: 

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

"CamelCASE" est converti en "camel_case"

  • lcfirst($camelCase) réduira le premier caractère (évite que la sortie convertie 'CamelCASE' commence par un trait de soulignement)
  • [A-Z] trouve des lettres majuscules
  • + traitera chaque majuscule consécutive comme un mot (évite que "CamelCASE" soit converti en camel_C_A_S_E)
  • Le second modèle et le remplacement sont pour ThoseSPECCases -> those_spec_cases au lieu de those_speccases
  • strtolower([…]) tourne la sortie en minuscule
20
buley

Le composant de sérialisation Symfony a un CamelCaseToSnakeCaseNameConverter qui a deux méthodes normalize() et denormalize(). Ceux-ci peuvent être utilisés comme suit:

$nameConverter = new CamelCaseToSnakeCaseNameConverter();

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

echo $nameConverter->denormalize('snake_case');
// outputs: snakeCase
19
mattvick

php n'offre pas de fonction intégrée pour cette afaik, mais voici ce que j'utilise

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

}

le séparateur peut être spécifié dans l'appel de fonction, vous pouvez donc l'appeler comme suit

$camelized="thisStringIsCamelized";
echo uncamelize($camelized,"_");
//echoes "this_string_is_camelized"
echo uncamelize($camelized,"-");
//echoes "this-string-is-camelized"
17
ekhaled
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_áááá_őőőő

(La "solution" acceptée est un échec épique ...)

8
inf3rno

Si vous recherchez une version PHP 5.4 et une réponse ultérieure, voici le code:

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

} 
6
shacharsol

"CamelCase" à "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, '_');
}
4
xiaojing

Pas du tout chic mais simple et rapide comme l'enfer:

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
4
Edakos

danielstjules/Stringy fournit une méthode pour convertir une chaîne de camelcase en snakecase.

s('TestUCase')->underscored(); // 'test_u_case'
3
Jimmy Ko

Laravel 5.6 fournit un moyen très simple de procéder:

 /**
 * 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;
}

Ce qu'il fait: s'il voit qu'il y a au moins une lettre majuscule dans la chaîne donnée, il utilise un positif lookahead pour rechercher un caractère (.) suivi d'une majuscule ((?=[A-Z])). Il remplace ensuite le caractère trouvé par sa valeur suivie du séparateur _.

3
Valdrinit

Donc, voici un one-liner:

strtolower(preg_replace('/(?|([a-z\d])([A-Z])|([^\^])([A-Z][a-z]))/', '$1_$2', $string));
3
seelts

Une version qui n’utilise pas regex peut être trouvée dans Alchitect source:

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);
}
3
Darrell Brogdon

Voici ma contribution à une question vieille de six ans avec Dieu sait combien de réponses ...

Il convertira tous les mots de la chaîne fournie qui sont dans camelcase en snakecase. Par exemple, "SuperSpecialAwesome et FizBuzz καιΚάτιΑκόμα" seront convertis en "super_special_awesome et fizz_buzz και_κάτι_ακόμα".

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

Le port direct depuis Rails (moins leur traitement spécial pour :: ou des acronymes) serait

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

Connaissant PHP, cela sera plus rapide que l'analyse manuelle utilisée dans les autres réponses données ici. L'inconvénient est que vous ne devez pas choisir quoi utiliser comme séparateur entre les mots, mais cela ne faisait pas partie de la question.

Vérifiez également le code source pertinent de Rails

Notez que ceci est destiné à être utilisé avec les identificateurs ASCII. Si vous devez le faire avec des caractères situés en dehors de la plage ASCII, utilisez le modificateur '/ u' pour preg_matchet utilisez mb_strtolower.

2
pilif

Si vous utilisez le framework Laravel, vous pouvez utiliser juste snake_case () method.

1
Marek Skiba
$str = 'FooBarBaz';

return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $str)); // foo_bar_baz
1
Omar Makled
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);
}
1
Kurt Zhong

Il y a une bibliothèque fournissant cette fonctionnalité:

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

Yii2 a la fonction différente de créer le mot snake_case de 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));
    }
1
Anil Chaudhari

Solution courte:

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

C'est ce que j'utilise pour la décamélisation:

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

  foreach(str_split($str) as $index => $char) {
     if(ord($char) >= 65 && ord($char) <= 90) {
        $capitals[] = $char;
        $replace[] = ($index > 0 ? $glue : '').strtolower($char);
     }
  }

  if(sizeof($capitals)) return str_replace($capitals, $replace, $str);

  return $str;
}
1
baldrs

C'est l'un des moyens les plus courts:

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

La bibliothèque open source TurboCommons contient une méthode polyvalente formatCase () dans la classe StringUtils, qui vous permet de convertir une chaîne en un grand nombre de formats de cas courants, tels que CamelCase, UpperCamelCase, LowerCamelCase, snake_case, Title Case, etc.

https://github.com/edertone/TurboCommons

Pour l'utiliser, importez le fichier phar dans votre projet et:

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

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

// will output 'camel_Case'
0
Jaume Mussons Abad

C'est facile d'utiliser les classes de filtres de Zend Filtres de mots :

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

facile >>> facile

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

facile >>> facile

HTML >>> html

simpleXML >>> simple_xml

PDFLoad >>> pdf_load

startMIDDLELast >>> start_middle_last

AString >>> a_string

Some4Numbers234 >>> some4_numbers234

TEST123String >>> test123_string

0
automatix

La pire réponse ici était si proche d'être la meilleure (utilisez un cadre). NO DON'T, jetez simplement un coup d'œil au code source. voir ce qu'un cadre bien établi utilise serait une approche beaucoup plus fiable (testée et éprouvée) Le framework Zend a des filtres Word qui répondent à vos besoins. La source .

voici quelques méthodes que j'ai adaptées à partir de la source. 

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");
0
TarranJones