Jak przekonwertować PascalCase na pascal_case?

115

Jeśli miałbym:

$string = "PascalCase";

potrzebuję

"pascal_case"

Czy PHP oferuje funkcję do tego celu?

openfrog
źródło
31
Technicznie pierwszy przykładowy ciąg to PascalCase.
Robin van Baalen
33
Drugi przykładowy ciąg jest znany jako snake_case .
Pang

Odpowiedzi:

163

Wypróbuj ten rozmiar:

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

Wynik:

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

To implementuje następujące zasady:

  1. Po sekwencji zaczynającej się od małej litery muszą następować małe litery i cyfry;
  2. Po sekwencji zaczynającej się od dużej litery może następować:
    • jedna lub więcej wielkich liter i cyfr (po których następuje koniec ciągu lub wielka litera, po której następuje mała litera lub cyfra, czyli początek następnej sekwencji); lub
    • jedna lub więcej małych liter lub cyfr.
cletus
źródło
9
Działa dla łańcuchów CamelCased (zgodnie z pytaniem openfrog), ale jeśli użyjesz go z ciągiem wejściowym, na przykład „r_id” (już „podkreślono”), przycina prefiks („r_”). Dobre rozwiązanie, ale zdecydowanie nie uniwersalne.
Martin
1
Ciekawe, dlaczego sprawdzasz, czy ciąg pasuje do ciągu z wielkimi literami? Jaka jest korzyść z zamiany tylko pierwszego znaku na małe litery (w przeciwieństwie do wszystkich znaków)?
Josh
1
Bardziej zwięzłe rozwiązanie, które może również obsługiwać wszystkie te przypadki użycia: stackoverflow.com/a/35719689/4328383
Syone
156

Krótsze rozwiązanie: podobne do edytora z uproszczonym wyrażeniem regularnym i naprawiającym problem „końcowego podkreślenia”:

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

Demo PHP | Regex Demo


Zwróć uwagę, że przypadki takie jak SimpleXMLzostaną przekonwertowane na simple_x_m_lprzy użyciu powyższego rozwiązania. Można to również uznać za niewłaściwe użycie notacji wielkości liter wielbłąda (poprawne byłoby SimpleXml) zamiast błędu algorytmu, ponieważ takie przypadki są zawsze niejednoznaczne - nawet poprzez zgrupowanie wielkich liter w jeden ciąg ( simple_xml) taki algorytm zawsze zawiedzie w innych przypadkach skrajnych podobne XMLHTMLConverterlub jednoliterowe słowa w pobliżu skrótów itp. Jeśli nie masz nic przeciwko (raczej rzadkim) skrajnym przypadkom i chcesz SimpleXMLpoprawnie obsłużyć , możesz użyć nieco bardziej złożonego rozwiązania:

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

Demo PHP | Regex Demo

Jan Jakeš
źródło
Nie wahaj się skomentować odpowiedzi Cletusa, wyszczególniając, które przypadki testowe zostały naprawione.
Mike B
3
Nie twierdzę, że jego rozwiązanie daje złe wyniki. Jego rozwiązanie jest po prostu niezwykle skomplikowane i nieskuteczne.
Jan Jakeš
1
Tak, akceptacja odpowiedzi jest zdecydowanie porażką. Rozwiązanie Jana jest niesamowite! Na marginesie, myślę, że to (lub niewielka zmiana) jest moim nowym, ulubionym testem kodowania dla programistów PHP, ponieważ liczba odpowiedzi udzielonych na to pytanie, które w rzeczywistości nie działają, jest niesamowita. Byłby to świetny sposób na wstępne filtrowanie. :-)
JamesG
stwierdził, że wyrażenie regularne użyte w tym rozwiązaniu jest znacznie bardziej kompletne: stackoverflow.com/questions/2559759/ ...
thoroc
2
Dobre rozwiązanie dla prostych przypadków użycia iw większości zwykłych przypadków wystarczy, ale zaakceptowane rozwiązanie może obsłużyć więcej przypadków użycia, na przykład „simpleXML” może zostać przekonwertowane na „simple_xml”, a nie „simple_x_m_l”
Syone
35

Zwięzłe rozwiązanie i poradzi sobie z trudnymi przypadkami użycia:

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

Może obsłużyć wszystkie te przypadki:

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

Możesz przetestować tę funkcję tutaj: http://syframework.alwaysdata.net/decamelize

Syone
źródło
@VivekVardhan, której części tego wyrażenia regularnego nie rozumiesz?
Syone
Uhm, myślę, że małe litery nie są literami wielbłąda jest efektem ubocznym, w przypadku, gdy ciąg nie jest w formacie wielbłąda, należy zwrócić oryginalny. Rzeczywiście, jeśli wyślesz „simple_Text”, otrzymasz Fail: simple_Test => simple_Test [simple_test]. Małe litery powinny być wykonane tylko i jeśli tylko oryginalny ciąg jest prawdziwym łańcuchem wielbłądów. Co myślisz o?
guido
24

Przeniesione z Ruby's String#camelizei 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); 
}

Jedną ze sztuczek, które mogły zostać pominięte w powyższych rozwiązaniach, jest modyfikator „e”, który powoduje preg_replaceobliczenie zastępczego ciągu znaków jako kodu PHP.

user644783
źródło
10
eFlag na preg_replacejest przestarzałe w PHP 5.5.
cdmckay
A tak przy okazji, nie ma ich też w Rubim, ale w bibliotece inflatora Railsów - kamelizuj i podkreślaj. api.rubyonrails.org/classes/ActiveSupport/Inflector.html
mahemoff
2
To się nie powiedzie w przypadku „ThisIsATest”. Wydaje się, że nie obsługuje dwóch kolejnych wielkich liter.
OnaBai,
Uwaga: możesz użyć lcfirst, aby zmienić pierwszą literę na małą, wtedy nie potrzebujesz ^|ani strlen.
Benubird
decamelize bez deprecation: gist.github.com/scones/e09c30e696246fda14578bcf8ab4910a
bułeczki
23

Symfony serializer Komponent ma CamelCaseToSnakeCaseNameConverter że ma dwie metody normalize()i denormalize(). Można ich używać w następujący sposób:

$nameConverter = new CamelCaseToSnakeCaseNameConverter();

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

echo $nameConverter->denormalize('snake_case');
// outputs: snakeCase
Mateusz
źródło
1
Strzec się! $nameConverter->normalize('CamelCase')wyjścia _camel_casew aktualnej wersji 3.2 komponentu Symfony Serializer.
spackmat
21

Większość rozwiązań jest tutaj ciężka. Oto czego używam:

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

„CamelCASE” jest konwertowany na „camel_case”

  • lcfirst($camelCase) obniży pierwszy znak (pozwala uniknąć konwersji danych wyjściowych „CamelCASE” na początek podkreślenia)
  • [A-Z] znajduje wielkie litery
  • + potraktuje każdą kolejną wielką literę jako słowo (unika konwersji „CamelCASE” na camel_C_A_S_E)
  • Drugi wzór i zamiana są dla ThoseSPECCases-> those_spec_caseszamiastthose_speccases
  • strtolower([…]) zamienia dane wyjściowe na małe litery
buley
źródło
3
Ale zmienia też CamelCased na _camel_cased.
acme
1
to jest świetne - po prostu dodaj podciąg zaczynający się od znaku 1, aby obejść ten problem.
Oddman
4
Doskonale! Wystarczy dodać lcfirstfunkcję do $ camelCase
Edakos,
Zaakceptowana odpowiedź będzie obsługiwać: TestUPSClass w test_ups_class, podczas gdy to zmieni ją w test_u_p_s_class, o czym należy pamiętać.
Mazzy
Ciąg wejściowy, który zaczyna się od pierwszego „słowa” z allcaps, zostanie nieoczekiwanie podzielony przez to rozwiązanie z powodu ucfirst()wywołania. USADollarSymbolstaje się u_sa_dollar_symbol Demo Nie polecam tego rozwiązania, ponieważ musi wykonać dwa przejścia przez ciąg wejściowy z wyrażeniem regularnym - znakiem nierafinowanego wzorca.
mickmackusa
19

php nie oferuje wbudowanej funkcji dla tego afaika, ale oto czego używam

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

}

rozdzielacz można określić w wywołaniu funkcji, więc możesz go wywołać w ten sposób

$camelized="thisStringIsCamelized";
echo uncamelize($camelized,"_");
//echoes "this_string_is_camelized"
echo uncamelize($camelized,"-");
//echoes "this-string-is-camelized"
ekhaled
źródło
2
To się nie powiedzie w przypadku „ThisIsATest”. Wydaje się, że nie obsługuje dwóch kolejnych wielkich liter.
OnaBai
Na pewno o czymś zapomniałeś, bo druga wymiana nic nie robi. Oprócz tego możesz łatwo dostosować go do Unicode mb_strtoloweri włączyć /uopcję preg_replace.
bodo
8

Musisz przepuścić przez to wyrażenie regularne, które pasuje do każdej dużej litery, z wyjątkiem sytuacji, gdy jest na początku, i zastąpić je podkreśleniem i tą literą. Oto rozwiązanie 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_áááá_őőőő

Jeśli nie jesteś pewien, jaką wielkością jest Twój ciąg, lepiej najpierw go sprawdzić, ponieważ ten kod zakłada, że ​​dane wejściowe to camelCasezamiast underscore_Caselubdash-Case , więc jeśli ostatnie litery mają duże litery, doda do nich podkreślenia.

Przyjęta odpowiedź od cletus jest zbyt skomplikowana i działa tylko w przypadku znaków łacińskich. Uważam, że to naprawdę złe rozwiązanie i zastanawiam się, dlaczego w ogóle zostało zaakceptowane. Konwersja TEST123Stringna test123_stringniekoniecznie jest ważnym wymaganiem. Utrzymałem to raczej w prostocie i rozdzieleniu ABCcccna a_b_cccczamiast, ab_ccccponieważ nie traci w ten sposób informacji, a konwersja wsteczna da dokładnie ten sam ciąg, od którego zaczęliśmy. Nawet jeśli chcesz to zrobić w inny sposób, stosunkowo łatwo jest napisać dla niego wyrażenie regularne z dodatnim lookbehind (?<!^)\p{Lu}\p{Ll}|(?<=\p{Ll})\p{Lu}lub dwoma wyrażeniami regularnymi bez lookbehind, jeśli nie jesteś ekspertem od wyrażeń regularnych. Nie ma potrzeby dzielenia go na podciągi, nie wspominając o decydowaniu, gdzie strtoloweri lcfirstgdzie użycie strtolowerbyłoby całkowicie w porządku.

inf3rno
źródło
Odpowiedzi zawierające tylko kod mają niską wartość w Stackoverflow, ponieważ niewiele robią, aby edukować / wspierać tysiące przyszłych badaczy.
mickmackusa
@mickmackusa Jeśli naukowcy nauczą się kodować z SO, to mamy poważny problem ...
inf3rno
Teraz, gdy masz już ten osobisty atak z systemu, popraw odpowiedź. Zakładając, że wiesz, jak działa Twoje rozwiązanie i dlaczego używasz tych modyfikatorów wzorców, nie widzę powodu, aby odmawiać tej społeczności wiedzy. Jeśli rozważasz pozostawienie bardziej złośliwych odpowiedzi, zapewniam cię, że nie przeszkadzają mi. W czasie, gdy zajmowałeś się komentowaniem, mogłeś uzupełnić swoją odpowiedź, mogliśmy usunąć nasze komentarze, a ja mogłem pójść gdzie indziej, aby pomóc tej witrynie.
mickmackusa
Oczywiście nie mam uprawnień do usuwania postów z 8 głosami za. Jeśli chcesz, możesz usunąć swoją odpowiedź, ale nie byłoby trudno po prostu ją poprawić, usuwając niepotrzebne modyfikatory wzorców i dodając wyjaśnienie. Osobiste ataki nie mają na mnie żadnego wpływu.
mickmackusa
@mickmackusa Nie sądzę, żebym też mógł go usunąć. Jeśli chcesz, możesz go edytować.
inf3rno
6

Jeśli szukasz wersji PHP 5.4 i późniejszej odpowiedzi, oto kod:

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

} 
shacharsol
źródło
camelize produkuje "SmsSent" dla sms_sent, potrzebujesz lcfirst
mik3fly-4steri5k
4

Wcale nie wyszukane, ale proste i szybkie jak diabli:

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
Edakos
źródło
++$izamiast $i++to trochę przyspieszyć;)
Mathieu Amiot
Odpowiedzi zawierające tylko kod mają niską wartość w Stackoverflow, ponieważ niewiele robią, aby edukować / wspierać tysiące przyszłych badaczy.
mickmackusa
4

„CamelCase” na „camel_case”:

function camelToSnake($camel)
{
    $snake = preg_replace('/[A-Z]/', '_$0', $camel);
    $snake = strtolower($snake);
    $snake = ltrim($snake, '_');
    return $snake;
}

lub:

function camelToSnake($camel)
{
    $snake = preg_replace_callback('/[A-Z]/', function ($match){
        return '_' . strtolower($match[0]);
    }, $camel);
    return ltrim($snake, '_');
}
xiaojing
źródło
Dziękuję Ci. Użyłem pierwszego podejścia, ale z myślnikami do wygenerowaniathis-kind-of-output
thexpand
3

Wersję, która nie używa wyrażenia regularnego, można znaleźć w źródle 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);
}
Darrell Brogdon
źródło
2
Tak wyglądałoby życie bez wyrażenia regularnego :-)
ekhaled
4
Heh, tak. RegEx z pewnością ma swoje zalety. :) Surowa prędkość nie jest jedną z nich.
Darrell Brogdon,
z jakiegoś powodu uzyskałem zabawne wyniki
mr1031011
Nie działa dla mnie na podstawie tego ciągu: "CamelCaseTestAAATestAA", powinien mieć: "camel_case_test_a_a_a_test_a_a", ma: "" camel_case_test_aest "...
Sybio
3

Więc tutaj jest jedna linijka:

strtolower(preg_replace('/(?|([a-z\d])([A-Z])|([^\^])([A-Z][a-z]))/', '$1_$2', $string));
seelts
źródło
Fajnie, ale konwertuje tylko pierwszy wygląd, więc polecam dodanie gmodyfikatora do tego wyrażenia regularnego.
acme
@acme, używam go bez gi działa dobrze dla mnie.
widzi
Z jakiegoś powodu w moim przypadku musiałem dodać rozszerzenie g. Ale nie pamiętam frazy, z którą testowałem.
acme
3

danielstjules / Stringy zapewnił metodę konwersji łańcucha znaków z wielbłąda na futerał węża.

s('TestUCase')->underscored(); // 'test_u_case'
Jimmy Ko
źródło
3

Laravel 5.6 zapewnia bardzo prosty sposób na zrobienie tego:

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

Co robi: jeśli widzi, że w danym ciągu znajduje się co najmniej jedna duża litera, używa dodatniego lookahead, aby wyszukać dowolny znak ( .), po którym następuje duża litera ( (?=[A-Z])). Następnie zastępuje znaleziony znak jego wartością, po której następuje separator _.

Valdrinium
źródło
Ta funkcja wydaje się teraz nazywać snake_case () i żyje w globalnej przestrzeni nazw.
Wotuu
2

Bezpośrednim portem z railsów (bez ich specjalnej obsługi dla :: lub akronimów) byłoby

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

Znając PHP, będzie to szybsze niż ręczne analizowanie, które ma miejsce w innych odpowiedziach podanych tutaj. Wadą jest to, że nie możesz wybrać, czego użyć jako separatora między słowami, ale nie było to częścią pytania.

Sprawdź również kod źródłowy odpowiednich railsów

Należy pamiętać, że jest to przeznaczone do użytku z identyfikatorami ASCII. Jeśli musisz to zrobić ze znakami spoza zakresu ASCII, użyj modyfikatora „/ u” preg_matchi użyj mb_strtolower.

pilif
źródło
Możesz, jeśli po prostu dodasz parametr zawierający żądany znak.
Fleshgrinder
2

Oto mój wkład w pytanie od sześciu lat, w którym Bóg wie, ile odpowiedzi ...

Konwertuje wszystkie słowa w podanym ciągu, które są w postaci wielbłąda, na futerał węża. Na przykład „SuperSpecialAwesome, a także FizBuzz καιΚάτιΑκόμα” zostaną przekonwertowane na „super_special_awesome, a także fizz_buzz και_κάτι_ακόμα”.

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

Yii2 ma inną funkcję, aby utworzyć słowo snake_case z 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));
    }
Anil Chaudhari
źródło
2

Krótkie rozwiązanie:

$subject = "PascalCase";
echo strtolower(preg_replace('/\B([A-Z])/', '_$1', $subject));
Gevorg Melkumyan
źródło
2

Miałem podobny problem, ale nie mogłem znaleźć żadnej odpowiedzi, która byłaby satysfakcjonująca, jak przekonwertować CamelCase na snake_case, unikając zduplikowanych lub zbędnych podkreśleń _ dla nazw z podkreśleniami lub wszystkimi skrótami .

Problem jest następujący:

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

Rozwiązanie, które napisałem, to proste wywołanie dwóch funkcji, małe litery i wyszukiwanie oraz zamiana na kolejne małe i duże litery:

strtolower(preg_replace("/([a-z])([A-Z])/", "$1_$2", $name));
MMS-y
źródło
Jak dotąd jest to najbardziej zwięzłe i przydatne rozwiązanie 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);
}
Kurt Zhong
źródło
Możesz uzyskać dostęp do znaków bezpośrednio za pomocą $name{$k}(lub $name[$k]), co wydłużyłoby twój kod, ale pozwala uniknąć dużego narzutu konwersji do iz tablicy.
bodo
Odpowiedzi zawierające tylko kod mają niską wartość w StackOverflow, ponieważ słabo wzmacniają / kształcą przyszłych badaczy. Twoje rozwiązanie, unikając łaski wyrażenia regularnego, jest bardzo uciążliwe i zawiłe. Dzielisz każdy znak i wykonujesz wiele iteracyjnych wywołań funkcji. Wyznaczanie pustego sznurka jako kleju nie jest konieczne. Nie zajmowałbym się tym rozwiązaniem w jednym ze swoich projektów, ponieważ nie ma tu elegancji, niskiej czytelności i n liczby niepotrzebnych wywołań funkcji.
mickmackusa
1

Najgorsza odpowiedź była tak bliska bycia najlepszą (użyj frameworka). NIE NIE, po prostu spójrz na kod źródłowy. zobaczenie, jakie zastosowania mają dobrze ugruntowane ramy, byłoby znacznie bardziej niezawodnym podejściem (wypróbowanym i przetestowanym). Framework Zend ma kilka filtrów słów, które odpowiadają Twoim potrzebom. Źródło .

oto kilka metod, które zaadaptowałem ze źródła.

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");
TarranJones
źródło
1

Istnieje biblioteka zapewniająca taką funkcjonalność:

SnakeCaseFormatter::run('CamelCase'); // Output: "camel_case"
Kolyunya
źródło
1
Myślę, że masz na myśli „Stworzyłem bibliotekę zapewniającą taką funkcjonalność”. Nie ma nic złego w autopromocji, ale nie ukrywaj tego.
icc97
1

To jeden z krótszych sposobów:

function camel_to_snake($input)
{
    return strtolower(ltrim(preg_replace('/([A-Z])/', '_\\1', $input), '_'));
}
Ayman Gado
źródło
Odpowiedzi zawierające tylko kod mają niską wartość w Stackoverflow, ponieważ niewiele robią, aby edukować / wspierać tysiące przyszłych badaczy.
mickmackusa
1
@mickmackusa - tysiące przyszłych badaczy będzie zainteresowanych elegancką, jednokierunkową wersją i będzie się uczyć.
Teson
Przykro mi, że przyjąłeś tę samolubną postawę. Z pewnością mogłeś dodać wyjaśnienie w czasie, jaki zajęło ci zaprojektowanie i wpisanie tej złośliwej odpowiedzi. Twoja odpowiedź powoduje trzy wywołania funkcji, ale inne wykonują zadanie w dwóch.
mickmackusa
1

Jak usunąć kamelizację bez użycia wyrażenia regularnego:

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

Edycja:

Jak bym to zrobił w 2019 roku:

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

A kiedy pojawi się PHP 7.4:

function toSnakeCase($str, $glue = '_') {
    return preg_replace_callback('/[A-Z]/', fn($matches) => $glue . strtolower($matches[0]), $str);
}
baldrs
źródło
1
Odpowiedzi zawierające tylko kod mają niską wartość w StackOverflow, ponieważ słabo wzmacniają / kształcą przyszłych badaczy. Wykonywanie od 1 do 3 wywołań funkcji dla każdego znaku w ciągu, a następnie dwóch kolejnych wywołań funkcji po zakończeniu pętli jest bardzo trudne. Nie znalazłbym rozwiązania przy tak słabej ekonomii.
mickmackusa
To przykład, jak można to zrobić bez użycia wyrażeń regularnych, a nie jak powinno być używane w produkcji, więc nie widzę twojego punktu poza tym, że narzekasz na odpowiedź 5lat / o, która ma jeden głos za i jest mało prawdopodobne, aby została zauważona przez żadnych badaczy.
baldrs
Zwracam uwagę na wszystkie posty, nie tylko te wysoko oceniane lub ostatnio. Nie narzekam, przedstawiam swoją krytykę, aby badacze z mniejszą wiedzą mogli lepiej zrozumieć różnicę między tą odpowiedzią a innymi odpowiedziami. Mogłeś wyjaśnić w swoim poście, że unikanie wyrażeń regularnych było jedynie akademickim wyzwaniem. To powiedziawszy, istnieją sposoby na zwiększenie wydajności tego procesu dzięki lepszym praktykom kodowania.
mickmackusa
0

Korzystanie z klas filtrów w Zend Word Filters jest łatwe :

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

----- podkreślenieToCamelCase -----

simple_test >>> SimpleTest

łatwe >>> łatwe

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

łatwe >>> łatwe

HTML >>> html

simpleXML >>> simple_xml

PDFLoad >>> pdf_load

startMIDDLELast >>> start_middle_last

AString >>> a_string

Some4Numbers234 >>> some4_numbers234

TEST123String >>> test123_string

automatix
źródło
0

Biblioteka TurboCommons typu open source zawiera metodę formatCase () ogólnego przeznaczenia w klasie StringUtils, która umożliwia konwersję ciągu znaków na wiele popularnych formatów liter, takich jak CamelCase, UpperCamelCase, LowerCamelCase, snake_case, Title Case i wiele innych.

https://github.com/edertone/TurboCommons

Aby z niego skorzystać, zaimportuj plik phar do swojego projektu i:

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

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

// will output 'camel_Case'
Jaume Mussons Abad
źródło
0
$str = 'FooBarBaz';

return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $str)); // foo_bar_baz
Omar Makled
źródło
1
Odpowiedzi zawierające tylko kod mają niską wartość w StackOverflow, ponieważ słabo wzmacniają / kształcą przyszłych badaczy.
mickmackusa
-1

JEŚLI mógłbyś zacząć od:

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

Następnie możesz przekonwertować na dowolny przypadek tylko za pomocą:

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

Lub w innych przypadkach:

$capitalized = str_replace("_", " ", $string); // Camel Case
$constant = strtoupper($string);               // CAMEL_CASE
$train = str_replace("_", "-", $snake);        // camel-case
Nuno Rafael Figueiredo
źródło