วิธีแปลง PascalCase เป็น pascal_case


116

ถ้าฉันมี:

$string = "PascalCase";

ฉันต้องการ

"pascal_case"

PHP มีฟังก์ชันสำหรับจุดประสงค์นี้หรือไม่?


31
ในทางเทคนิคสตริงตัวอย่างแรกคือ PascalCase
Robin van Baalen

33
และสตริงตัวอย่างที่สองเป็นที่รู้จักกันsnake_case
ปาง

คำตอบ:


164

ลองใช้ขนาด:

$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" ("underscored" อยู่แล้ว) มันจะตัดส่วนนำหน้า ("r_") ทางออกที่ดี แต่ไม่เป็นสากลแน่นอน
Martin

1
สงสัยว่าทำไมคุณถึงตรวจสอบว่าสตริงตรงกับสตริงตัวพิมพ์ใหญ่ทั้งหมดหรือไม่? ประโยชน์ของการแปลงเพียงอักขระตัวแรกเป็นตัวพิมพ์เล็ก (เมื่อเทียบกับอักขระทั้งหมด)
Josh

1
โซลูชันที่รัดกุมยิ่งขึ้นซึ่งสามารถจัดการกับกรณีการใช้งานเหล่านี้ได้: stackoverflow.com/a/35719689/4328383
Syone

156

วิธีแก้ปัญหาที่สั้นกว่า: คล้ายกับตัวแก้ไขที่มีนิพจน์ทั่วไปที่เรียบง่ายและแก้ไขปัญหา "ต่อท้ายขีดล่าง":

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

PHP Demo | Regex Demo


โปรดทราบว่ากรณีเช่นSimpleXMLนี้จะถูกแปลงเป็นการsimple_x_m_lใช้โซลูชันข้างต้น นอกจากนี้ยังถือได้ว่าเป็นการใช้สัญกรณ์กรณีอูฐที่ไม่ถูกต้อง (ถูกต้องSimpleXml) แทนที่จะเป็นข้อบกพร่องของอัลกอริทึมเนื่องจากกรณีดังกล่าวมักจะคลุมเครือ - แม้จะจัดกลุ่มอักขระตัวพิมพ์ใหญ่เป็นสตริงเดียว ( simple_xml) อัลกอริทึมดังกล่าวจะล้มเหลวในกรณีอื่น ๆ เสมอ คำเหมือนXMLHTMLConverterหรือคำเดียวที่อยู่ใกล้ตัวย่อเป็นต้นหากคุณไม่สนใจเกี่ยวกับขอบกรณี (ค่อนข้างหายาก) และต้องการจัดการSimpleXMLอย่างถูกต้องคุณสามารถใช้วิธีแก้ปัญหาที่ซับซ้อนกว่านี้เล็กน้อย:

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

PHP Demo | Regex Demo


อย่าลังเลที่จะแสดงความคิดเห็นเกี่ยวกับคำตอบของ cletus ซึ่งมีรายละเอียดกรณีทดสอบที่คุณแก้ไข
Mike B

3
ฉันไม่ได้บอกว่าวิธีแก้ปัญหาของเขาให้ผลลัพธ์ที่ผิด วิธีแก้ปัญหาของเขานั้นซับซ้อนและไม่ได้ผล
Jan Jakeš

1
ใช่คำตอบที่ยอมรับคือความล้มเหลวอย่างแน่นอน วิธีแก้ของแจนสุดยอดมาก! ฉันคิดว่านี่ (หรือรูปแบบเล็กน้อย) เป็นการทดสอบการเข้ารหัสใหม่ที่ฉันชอบสำหรับนักพัฒนา PHP เนื่องจากจำนวนคำตอบที่ให้ไว้สำหรับคำถามนี้ซึ่งใช้ไม่ได้จริงนั้นเหลือเชื่อมาก มันจะเป็นวิธีที่ดีในการกรองข้อมูลเบื้องต้น :-)
JamesG

พบว่า regex ที่ใช้ในโซลูชันนี้สมบูรณ์กว่ามาก: stackoverflow.com/questions/2559759/…
thoroc

2
ทางออกที่ดีสำหรับกรณีการใช้งานที่เรียบง่ายและในกรณีปกติส่วนใหญ่ก็เพียงพอแล้ว แต่โซลูชันที่ยอมรับสามารถรองรับกรณีการใช้งานได้มากกว่าตัวอย่างเช่น "simpleXML" อาจถูกแปลงเป็น "simple_xml" และไม่ใช่ "simple_x_m_l"
Syone

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


@VivekVardhan ส่วนไหนของ regex ที่คุณไม่เข้าใจ?
Syone

อืมฉันคิดว่าการลดขนาดสตริงที่ไม่ใช่อูฐเป็นผลข้างเคียงในกรณีที่สตริงไม่อยู่ในรูปแบบเคสอูฐควรส่งคืนสตริงเดิม ไม่ระบุหากคุณส่ง "simple_Text" คุณจะได้รับ Fail: simple_Test => simple_Test [simple_test] ควรสร้างสตริงตัวลดลงเท่านั้นและในกรณีที่สตริงดั้งเดิมเท่านั้นที่เป็นสตริงเคสอูฐจริง สิ่งที่คุณคิดเกี่ยวกับ?
guido

24

ย้ายมาจาก Ruby 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


10
eธงpreg_replaceจะถูกเลิกใช้ใน PHP 5.5
cdmckay

BTW สิ่งเหล่านี้ไม่ได้อยู่ใน Ruby แต่อยู่ในไลบรารี inflector ของ Rails - camelize และขีดล่าง api.rubyonrails.org/classes/ActiveSupport/Inflector.html
mahemoff

2
สิ่งนี้ล้มเหลวสำหรับ "ThisIsATest" ดูเหมือนว่าจะไม่รองรับตัวพิมพ์ใหญ่สองตัวติดต่อกัน
OnaBai

เพียงแค่ทราบ: คุณสามารถใช้ lcfirst ที่จะได้รับตัวอักษรตัวแรกจะเป็นตัวพิมพ์เล็กแล้วคุณไม่จำเป็นต้องใช้หรือ^| strlen
Benubird

decamelize โดยไม่ต้องเลิกใช้: 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([…]) เปลี่ยนเอาต์พุตเป็นเคสที่ต่ำกว่า

3
แต่ยังเปลี่ยน CamelCased เป็น _camel_cased
acme

1
สิ่งนี้ยอดเยี่ยม - เพียงแค่เพิ่ม Substr โดยเริ่มต้นที่ char 1 เพื่อแก้ไขปัญหานั้น
Oddman

4
Excelent! เพียงแค่ต้องเพิ่มlcfirstฟังก์ชันให้กับ $ camelCase
Edakos

คำตอบที่ยอมรับจะจัดการ: TestUPSClass เป็น test_ups_class ในขณะที่สิ่งนี้จะเปลี่ยนเป็น test_u_p_s_class ซึ่งเป็นสิ่งที่ควรคำนึงถึง
Mazzy

สตริงอินพุตที่ขึ้นต้นด้วย allcaps แรก "word" จะถูกแยกโดยไม่คาดคิดโดยโซลูชันนี้เนื่องจากการucfirst()เรียก USADollarSymbolกลายเป็นu_sa_dollar_symbol Demoฉันไม่แนะนำวิธีแก้ปัญหานี้เพราะต้องทำการส่งผ่านสองครั้งผ่านสตริงอินพุตด้วย regex ซึ่งเป็นสัญลักษณ์ของรูปแบบที่ไม่ถูกปรับแต่ง
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"

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เพราะไม่ข้อมูลไม่สูญเสียด้วยวิธีนี้และการแปลงย้อนกลับจะให้สายเดียวกันที่แน่นอนเราเริ่มต้นด้วย แม้ว่าคุณจะต้องการใช้วิธีอื่น แต่การเขียน regex นั้นเป็นเรื่องง่ายโดยมีลักษณะเชิงบวกอยู่ข้างหลัง(?<!^)\p{Lu}\p{Ll}|(?<=\p{Ll})\p{Lu}หรือregex สองรายการโดยไม่ต้องมองไปข้างหลังหากคุณไม่ใช่ผู้เชี่ยวชาญ regex ไม่จำเป็นต้องแยกออกเป็นสตริงย่อยไม่ต้องพูดถึงการตัดสินใจระหว่างstrtolowerและlcfirstที่ใช้ก็strtolowerจะดีอย่างสมบูรณ์


คำตอบที่ใช้รหัสอย่างเดียวมีมูลค่าต่ำใน Stackoverflow เนื่องจากมีการให้ความรู้ / เพิ่มศักยภาพแก่นักวิจัยในอนาคตหลายพันคน
mickmackusa

@mickmackusa หากนักวิจัยเรียนรู้วิธีการเขียนโค้ดจาก SO แสดงว่าเรามีปัญหาร้ายแรง ...
inf3rno

เมื่อคุณได้รับการโจมตีส่วนบุคคลนั้นออกจากระบบของคุณแล้วโปรดปรับปรุงคำตอบของคุณ สมมติว่าคุณรู้ว่าโซลูชันของคุณทำงานอย่างไรและเหตุใดคุณจึงใช้ตัวปรับแต่งรูปแบบเหล่านั้นฉันไม่เห็นเหตุผลที่ดีที่จะระงับความรู้จากชุมชนนี้ ในกรณีที่คุณกำลังพิจารณาที่จะตอบกลับที่น่ารังเกียจเพิ่มเติมฉันรับรองว่าพวกเขาไม่รบกวนฉัน ในช่วงเวลาที่คุณแสดงความคิดเห็นคุณสามารถตอบเสร็จเราสามารถลบความคิดเห็นของเราและฉันสามารถไปที่อื่นเพื่อช่วยไซต์นี้ได้
mickmackusa

แน่นอนฉันไม่มีสิทธิ์ลบโพสต์ที่มี 8 upvotes หากคุณต้องการคุณสามารถลบคำตอบของคุณได้ แต่คงไม่ใช่เรื่องยากที่จะปรับปรุงโดยการลบตัวปรับแต่งรูปแบบที่ไม่จำเป็นและเพิ่มคำอธิบาย การโจมตีส่วนบุคคลไม่มีผลกับฉัน
mickmackusa

@mickmackusa ไม่คิดว่าจะลบได้เหมือนกัน อย่าลังเลที่จะแก้ไขหากคุณต้องการ
inf3rno

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

} 

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

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

ขอบคุณ. ฉันใช้แนวทางแรก แต่ใช้ขีดกลางเพื่อสร้างthis-kind-of-output
thexpand

3

เวอร์ชันที่ไม่ใช้ regex สามารถพบได้ในแหล่งข้อมูล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
นี่คือสิ่งที่ชีวิตจะเป็นอย่างไรหากไม่มี regex :-)
ekhaled

4
เฮ้ใช่ RegEx มีข้อดีอย่างแน่นอน :) ความเร็วดิบไม่ใช่หนึ่งในนั้น
Darrell Brogdon

ได้ผลลัพธ์ที่ตลกด้วยเหตุผลบางประการ
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));

ดี แต่จะแปลงเฉพาะลักษณะแรกเท่านั้นดังนั้นฉันขอแนะนำให้เพิ่มgตัวปรับแต่งให้กับ regex นี้
acme

@acme ฉันไม่ใช้มันgและมันก็ใช้ได้ดีสำหรับฉัน
seelts

ด้วยเหตุผลบางอย่างในกรณีของฉันฉันต้องเพิ่มไฟล์g. แต่ฉันจำวลีที่ทดสอบด้วยไม่ได้
acme


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])) จากนั้นก็เข้ามาแทนที่ตัวอักษรที่พบด้วยมูลค่าของมันตามมาด้วย _separactor


ตอนนี้ดูเหมือนว่าฟังก์ชันนี้จะเรียกว่า 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 แล้วสิ่งนี้จะเร็วกว่าการแยกวิเคราะห์ด้วยตนเองที่เกิดขึ้นในคำตอบอื่น ๆ ที่ให้ไว้ที่นี่ ข้อเสียคือคุณไม่สามารถเลือกสิ่งที่จะใช้เป็นตัวคั่นระหว่างคำได้ แต่นั่นไม่ใช่ส่วนหนึ่งของคำถาม

ตรวจสอบซอร์สโค้ดรางที่เกี่ยวข้องด้วย

โปรดทราบว่าสิ่งนี้มีไว้สำหรับใช้กับตัวระบุ ASCII หากคุณจำเป็นต้องทำเช่นนี้กับตัวละครนอกช่วง ASCII ใช้ปรับปรุง '/ u' สำหรับการและการใช้งานpreg_matchmb_strtolower


คุณสามารถทำได้ถ้าคุณเพียงแค่เพิ่มพารามิเตอร์ที่มีอักขระที่ต้องการ
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'
    )
);

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

ฉันมีปัญหาที่คล้ายกัน แต่ไม่พบคำตอบใด ๆ ที่ตรงตามวิธีการแปลง 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
Mr.Shan0

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

คำตอบที่ใช้รหัสอย่างเดียวมีมูลค่าต่ำใน StackOverflow เนื่องจากพวกเขาทำงานได้ไม่ดีในการเพิ่มขีดความสามารถ / ให้ความรู้แก่นักวิจัยในอนาคต วิธีแก้ปัญหาของคุณในขณะที่หลีกเลี่ยงความสง่างามของ regex นั้นหนักมือและซับซ้อนมาก คุณกำลังแยกอักขระทุกตัวและทำการเรียกฟังก์ชันซ้ำหลาย ๆ ไม่จำเป็นต้องระบุสตริงว่างเนื่องจากกาว ฉันจะไม่ให้ความบันเทิงกับโซลูชันนี้ในหนึ่งในโครงการของฉันเนื่องจากไม่มีความสง่างามความสามารถในการอ่านต่ำและการเรียกใช้ฟังก์ชันที่ไม่จำเป็นจำนวนn
mickmackusa

1

คำตอบที่แย่ที่สุดในที่นี้ใกล้เคียงกับการเป็นสิ่งที่ดีที่สุด (ใช้กรอบ) ไม่ทำเพียงแค่ดูที่ซอร์สโค้ด การดูว่าเฟรมเวิร์กที่ได้รับการยอมรับใช้นั้นเป็นแนวทางที่เชื่อถือได้มากกว่า (ทดลองและทดสอบแล้ว) Zend framework มีตัวกรองคำที่เหมาะกับความต้องการของคุณ แหล่ง

นี่คือสองวิธีที่ฉันปรับเปลี่ยนจากแหล่งที่มา

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

1

มีห้องสมุดที่ให้ฟังก์ชันนี้:

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

1
ฉันคิดว่าคุณหมายถึง "ฉันได้สร้างห้องสมุดที่มีฟังก์ชันนี้" ไม่มีอะไรผิดเกี่ยวกับการโปรโมตตนเอง แต่อย่าปิดบัง
icc97


1

นี่เป็นวิธีที่สั้นกว่าวิธีหนึ่ง:

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

คำตอบที่ใช้รหัสอย่างเดียวมีมูลค่าต่ำใน Stackoverflow เนื่องจากมีการให้ความรู้ / เพิ่มศักยภาพแก่นักวิจัยในอนาคตหลายพันคน
mickmackusa

1
@mickmackusa - งานวิจัยในอนาคตหลายพันชิ้นจะสนใจในหนึ่งซับที่สง่างามและให้ความรู้กับตัวเอง
Teson

ฉันขอโทษที่คุณใช้ท่าทางเห็นแก่ตัวแบบนั้น แน่นอนคุณสามารถเพิ่มคำอธิบายในช่วงเวลาที่คุณต้องออกแบบและพิมพ์คำตอบที่น่ารังเกียจนั้นได้ คำตอบของคุณเรียกใช้ฟังก์ชันสามครั้ง แต่คนอื่น ๆ ทำงานเป็นสองอย่าง
mickmackusa

1

วิธีการถอดอูฐโดยไม่ใช้ 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:

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

1
คำตอบที่ใช้รหัสอย่างเดียวมีมูลค่าต่ำใน StackOverflow เนื่องจากพวกเขาทำงานได้ไม่ดีในการเพิ่มขีดความสามารถ / ให้ความรู้แก่นักวิจัยในอนาคต การเรียกใช้ฟังก์ชัน 1 ถึง 3 กับทุกอักขระในสตริงจากนั้นเรียกฟังก์ชันอีกสองฟังก์ชันหลังจากการวนซ้ำเสร็จสิ้นนั้นใช้งานหนักมาก ฉันจะไม่สนุกกับการแก้ปัญหาด้วยเศรษฐกิจที่ย่ำแย่เช่นนี้
mickmackusa

เป็นตัวอย่างวิธีที่สามารถทำได้โดยไม่ต้องใช้นิพจน์ทั่วไปไม่ใช่วิธีที่ควรใช้ในการผลิตดังนั้นฉันจึงไม่เห็นประเด็นของคุณนอกจากที่คุณบ่นเกี่ยวกับคำตอบ 5y / o ที่มีการโหวตเพิ่มขึ้นหนึ่งรายการและไม่น่าจะมีให้เห็น นักวิจัยทุกคน
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

startMIDDLEL สุดท้าย >>> start_middle_last

AString >>> a_string

Some4Numbers234 >>> some4_numbers234

TEST123String >>> test123_string


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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.