如何将 PascalCase 转换为 Snake_case?

如果我有:

$string = "PascalCase";

我需要

"pascal_case"

PHP 是否为此提供了一个函数?

124621 次浏览

Php 并没有为这个 faik 提供一个内置的函数,但是这里是我使用的

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"

试试这个大小:

$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. 以大写字母开头的序列后面可以是:
    • 一个或多个大写字母和数字(后跟字符串末尾或大写字母后跟小写字母或数字即下一个序列的开头) ; 或
    • 一个或多个小写字母或数字。

不使用正则表达式的版本可以在 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);
}

从 Ruby 的 String#camelizeString#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 代码进行计算。

您需要通过它运行一个匹配每个大写字母的正则表达式,除非它是在开头,并将其替换为下划线加上该字母。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_Casedash-Case,所以如果后者有大写字母,它将为它们添加下划线。

Cletus 给出的答案过于复杂,而且只适用于拉丁字符。我发现这是一个非常糟糕的解决方案,我想知道为什么会被接受。将 TEST123String转换为 test123_string不一定是有效的要求。我宁愿保持它的简单性,并将 ABCccc分隔成 a_b_cccc而不是 ab_cccc,因为它不会以这种方式丢失信息,而且向后转换将给出与我们开始时完全相同的字符串。即使你想用另一种方法来做,如果你不是一个正则表达式专家,你也可以相对容易地为它编写一个正则表达式,在 (?<!^)\p{Lu}\p{Ll}|(?<=\p{Ll})\p{Lu}或两个正则表达式后面进行正向查找,而不需要进行正则表达式查找。没有必要把它拆分成子字符串,更不用说在 strtolowerlcfirst之间决定,只使用 strtolower是完全可以的。

如何在不使用 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;
}

编辑:

2019年我该怎么做:

PHP 7.3及以前版本:

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


}

PHP 7.4 + :

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

这里的大多数解决方案都让人觉得笨手笨脚。以下是我使用的方法:

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

“ CamelCASE”转换为“ CamelCASE”

  • lcfirst($camelCase)将降低第一个字符(避免‘ CamelCASE’转换后的输出以下划线开始)
  • [A-Z]找到大写字母
  • +将把每个连续的大写字母视为一个单词(避免将‘ CamelCASE’转换为驼峰 _ C _ A _ S _ E)
  • 第二种模式和替换是 ThoseSPECCases-> those_spec_cases而不是 those_speccases
  • strtolower([…])将输出转换为小写

一个简短的解决方案: 类似于 编辑的,它有一个简化的正则表达式,并解决了“拖尾-下划线”问题:

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

PHP 演示 正则表达式演示


请注意,像 SimpleXML这样的情况将使用上述解决方案转换为 simple_x_m_l。这也可以被认为是骆驼大小写符号的错误使用(正确的应该是 SimpleXml) ,而不是算法的错误,因为这种情况总是模棱两可的——即使把大写字符分组成一个字符串(simple_xml) ,这种算法也总是会在其他边缘情况下失败,比如 XMLHTMLConverter或者靠近缩写的单字母单词等等。如果您不介意(相当罕见的)边缘情况,并希望正确处理 SimpleXML,您可以使用一个更复杂的解决方案:

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

PHP 演示 正则表达式演示

来自 Rail 的直接端口(减去它们对: : 或首字母缩写的特殊处理)将为

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 之后,这将比在这里给出的其他答案中发生的手动解析更快。缺点是你不能选择用什么作为单词之间的分隔符,但这不是问题的一部分。

再检查一下 相关轨道源代码

注意,这是用于 ASCII 标识符的。如果需要对 ASCII 范围之外的字符执行此操作,请使用 preg_match的“/u”修饰符并使用 mb_strtolower

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

使用 Zend 单词过滤器的 Filter 类很容易:

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

- -

Simple _ test > > > SimpleTest

简单

Html > > > HTML

Simple _ xml > > > SimpleXml

Pdf _ load > > > PDfLoad

Start _ mid _ last > > StartMiddleLast

A _ string > > > AString

Some4 _ numbers234 > > > Some4Numbers234

Test123 _ string > > Test123String

- -

SimpleTest > > > simple _ test

简单

HTML > > > HTML

SimpleXML > > > simple _ xml

PDFLoad > > > pdf _ load

StartMIDDLELast > > start _ mid _ last

字符串 > > a _ string

Some4Numbers234 > > > some4 _ numbers234

TEST123String > > test123 _ string

一点也不花哨,简单而迅速:

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

因此,这里有一句俏皮话:

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

如果你正在寻找一个 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
);


}

如果你可以这样开始:

$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

这里最糟糕的答案几乎是最好的(使用框架)。不,不要,只要看一下源代码就可以了。看看一个完善的框架使用什么将是一个更可靠的方法(经过尝试和测试)。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");

一个简洁的解决方案,可以处理一些棘手的用例:

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

有一个 图书馆提供这种功能:

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

Symfony 序列化组件有一个 名称转换器,它有两个方法 normalize()denormalize()。这两个方法可以使用如下:

$nameConverter = new CamelCaseToSnakeCaseNameConverter();


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


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

如果使用 Laravel 框架,只需使用 Snake _ case ()方法即可。

Danielstjules/Stringy 提供了一个将字符串从 camelcase 转换为 snakecase 的方法。

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

这是我对一个六年前的问题的贡献,天知道有多少答案..。

它将把所提供的字符串中的所有单词从驼形转换为蛇形。例如,“ SuperSpecialAwesome,以及 FizBuzz κα奴隶般的庆祝 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *。

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

开源的 TurboCommons 库在 StringUtils 类中包含一个通用的 format Case ()方法,它允许您将字符串转换为许多常见的 Case 格式,比如 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'
$str = 'FooBarBaz';


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

“ CamelCase”到“ CamelCase”:

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

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]))的任何字符(.)。然后,它将找到的字符替换为它的值,后跟分隔符 _

Yii2与 CamelCase 有不同的功能,可以组成单词 Snake _ case。

    /**
* 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 camel_to_snake($input)
{
return strtolower(ltrim(preg_replace('/([A-Z])/', '_\\1', $input), '_'));
}

简单来说:

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

我也遇到过类似的问题,但是找不到任何答案来满足如何将 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));

如果不使用 Composer for PHP,那么就是在浪费时间。

composer require doctrine/inflector
use Doctrine\Inflector\InflectorFactory;


// Couple ways to get class name:


// If inside a parent class
$class_name = get_called_class();


// Or just inside the class
$class_name = get_class();


// Or straight get a class name
$class_name = MyCustomClass::class;


// Or, of course, a string
$class_name = 'App\Libs\MyCustomClass';


// Take the name down to the base name:
$class_name = end(explode('\\', $class_name)));


$inflector = InflectorFactory::create()->build();


$inflector->tableize($class_name); // my_custom_class

Https://github.com/doctrine/inflector/blob/master/docs/en/index.rst

使用 Symfony String

composer require symfony/string
use function Symfony\Component\String\u;


u($string)->snake()->toString()

如果你正在使用 Laravel 框架,有一个更简单的内置方法:

$converted = Str::snake('fooBar'); // -> foo_bar

点击这里查看文档: Https://laravel.com/docs/9.x/helpers#method-snake-case