Teilen Sie camelCase-Wörter mit php preg_match (Regular Expression) in Wörter auf


71

Wie würde ich vorgehen, um das Wort zu teilen:

oneTwoThreeFour

in ein Array, damit ich bekommen kann:

one Two Three Four

mit preg_match?

Ich habe es satt, aber es gibt nur das ganze Wort

$words = preg_match("/[a-zA-Z]*(?:[a-z][a-zA-Z]*[A-Z]|[A-Z][a-zA-Z]*[a-z])[a-zA-Z]*\b/", $string, $matches)`;

Vielleicht kann meine Frage Ihnen helfen, ich habe gestern das Gleiche gefragt, aber über Java stackoverflow.com/questions/4502273/…
Gondim

Antworten:


81

Sie können auch verwenden preg_match_allals:

preg_match_all('/((?:^|[A-Z])[a-z]+)/',$str,$matches);

Erläuterung:

(        - Start of capturing parenthesis.
 (?:     - Start of non-capturing parenthesis.
  ^      - Start anchor.
  |      - Alternation.
  [A-Z]  - Any one capital letter.
 )       - End of non-capturing parenthesis.
 [a-z]+  - one ore more lowercase letter.
)        - End of capturing parenthesis.

Würde die nicht erfassende Gruppe nicht dazu führen, dass das Ergebnis [eins, wo, drei, unser] ist?
Aaron J Lang

2
@AaronJLang nein, da die äußeren Klammern die GANZE Gruppe erfassen, einschließlich der Untergruppe. Es ist eine Untergruppe, die er nicht überladen möchte.
Eli Gassert

2
Dies schlug für mich mit "TestID" fehl, indem Folgendes verwendet wurde: "preg_match_all ('/ ((?: ^ | [AZ]) [az] +) /', $ key, $ Matches); die (implode ('', $ Matches [) 0])); " weil es das Thema CONSECUTIVE CAPS nicht mag. Ich musste Falländerungen mit Leerzeichen teilen und die Lösung von @ blak3r funktionierte für mich: stackoverflow.com/a/17122207/539149
Zack Morris

1
Eine bessere Lösung für solche Zeichenfolgen HTMLParserfunktioniert: stackoverflow.com/a/6572999/1697320 .
Maciej Sz

Wie von @TarranJones festgelegt (obwohl nicht zu klar formuliert), benötigen Sie keine äußere Klammer. Eine übereinstimmende Zeichenfolge von '/(?:^|[A-Z])[a-z]+/'würde ausreichen, um ein Array (anstelle von zwei) zu erzeugen. Dies liegt daran, dass preg_match_all()automatisch alle Instanzen des Spiels erfasst werden, ohne dass Sie dies speziell festlegen müssen.
Cartbeforehorse

77

Sie können verwenden preg_splitals:

$arr = preg_split('/(?=[A-Z])/',$str);

Sehen Sie es

Grundsätzlich teile ich die Eingabezeichenfolge kurz vor dem Großbuchstaben. Die verwendete Regex (?=[A-Z])entspricht dem Punkt unmittelbar vor einem Großbuchstaben.


1
schöne Lösung
Diego Sagrera

54

Ich weiß, dass dies eine alte Frage mit einer akzeptierten Antwort ist, aber meiner Meinung nach gibt es eine bessere Lösung:

<?php // test.php Rev:20140412_0800
$ccWord = 'NewNASAModule';
$re = '/(?#! splitCamelCase Rev:20140412)
    # Split camelCase "words". Two global alternatives. Either g1of2:
      (?<=[a-z])      # Position is after a lowercase,
      (?=[A-Z])       # and before an uppercase letter.
    | (?<=[A-Z])      # Or g2of2; Position is after uppercase,
      (?=[A-Z][a-z])  # and before upper-then-lower case.
    /x';
$a = preg_split($re, $ccWord);
$count = count($a);
for ($i = 0; $i < $count; ++$i) {
    printf("Word %d of %d = \"%s\"\n",
        $i + 1, $count, $a[$i]);
}
?>

Beachten Sie, dass dieser reguläre Ausdruck (wie die '/(?=[A-Z])/'Lösung von codaddict - die wie ein Zauber für wohlgeformte camelCase-Wörter funktioniert) nur einer Position innerhalb der Zeichenfolge entspricht und überhaupt keinen Text verbraucht. Diese Lösung hat den zusätzlichen Vorteil, dass sie auch für nicht so gut geformte Pseudo-Camelcase-Wörter wie: StartsWithCapund: korrekt funktioniert hasConsecutiveCAPS.

Eingang:

oneTwoThreeFour
StartsWithCap
hasConsecutiveCAPS
NewNASAModule

Ausgabe:

Word 1 of 4 = "one"
Word 2 of 4 = "Two"
Word 3 of 4 = "Three"
Word 4 of 4 = "Four"

Word 1 of 3 = "Starts"
Word 2 of 3 = "With"
Word 3 of 3 = "Cap"

Word 1 of 3 = "has"
Word 2 of 3 = "Consecutive"
Word 3 of 3 = "CAPS"

Word 1 of 3 = "New"
Word 2 of 3 = "NASA"
Word 3 of 3 = "Module"

Bearbeitet: 2014-04-12: Regex- , Skript- und Testdaten wurden geändert, um den "NewNASAModule"Fall korrekt aufzuteilen: case (als Antwort auf den Kommentar von rr).


Dies ist eine viel bessere Lösung, funktioniert beim ersten Mal (andere haben dem Array leere Werte hinzugefügt, diese ist perfekt! Danke! +1
Anil

1
Es scheint ein Problem mit Zeichenfolgen wie NewNASAModule(Ausgaben : [New, NASAModule]; Ich würde erwarten [New, NASA, Module])
rr-

1
@rr - Ja du bist richtig. Siehe meine andere aktualisierte Antwort, die teilt: NewNASAModulerichtig: RegEx, um camelCase oder TitleCase (erweitert) zu
teilen

Es werden keine Fälle mit Ziffern behandelt. Aus irgendeinem Grund ignorieren auch andere Antwortende diese grundlegende Tatsache. ZB "Css3Transform" oder ähnliches
Onkeltem

15

Die Antwort von ridgerunner funktioniert zwar hervorragend, scheint jedoch nicht mit All-Caps-Teilzeichenfolgen zu funktionieren, die in der Mitte des Satzes erscheinen. Ich benutze folgendes und es scheint in Ordnung damit umzugehen:

function splitCamelCase($input)
{
    return preg_split(
        '/(^[^A-Z]+|[A-Z][^A-Z]+)/',
        $input,
        -1, /* no limit for replacement count */
        PREG_SPLIT_NO_EMPTY /*don't return empty elements*/
            | PREG_SPLIT_DELIM_CAPTURE /*don't strip anything from output array*/
    );
}

Einige Testfälle:

assert(splitCamelCase('lowHigh') == ['low', 'High']);
assert(splitCamelCase('WarriorPrincess') == ['Warrior', 'Princess']);
assert(splitCamelCase('SupportSEELE') == ['Support', 'SEELE']);
assert(splitCamelCase('LaunchFLEIAModule') == ['Launch', 'FLEIA', 'Module']);
assert(splitCamelCase('anotherNASATrip') == ['another', 'NASA', 'Trip']);

13

Eine funktionalisierte Version von @ ridgerunners Antwort.

/**
 * Converts camelCase string to have spaces between each.
 * @param $camelCaseString
 * @return string
 */
function fromCamelCase($camelCaseString) {
        $re = '/(?<=[a-z])(?=[A-Z])/x';
        $a = preg_split($re, $camelCaseString);
        return join($a, " " );
}

8
$string = preg_replace( '/([a-z0-9])([A-Z])/', "$1 $2", $string );

Der Trick ist ein wiederholbares Muster. $ 1 $ 2 $ 1 $ 2 oder niedriger UPPERlower UPPERlower usw. "Hallo Welt" entspricht HelloWorld als $ 2 $ 1 $ 2 $ 1 oder erneut als "Hallo Welt". Dann können Sie das erste Wort in Kleinbuchstaben schreiben oder im Leerzeichen explodieren lassen oder ein _ oder ein anderes Zeichen verwenden, um sie getrennt zu halten.

Kurz und einfach.


4

Bei der Ermittlung des besten Musters für Ihr Projekt müssen Sie die folgenden Musterfaktoren berücksichtigen:

  1. Genauigkeit (Robustheit) - ob das Muster in allen Fällen korrekt und einigermaßen zukunftssicher ist
  2. Effizienz - Das Muster sollte direkt und bewusst sein und unnötige Arbeit vermeiden
  3. Kürze - Das Muster sollte geeignete Techniken verwenden, um unnötige Zeichenlängen zu vermeiden
  4. Lesbarkeit - Das Muster sollte so einfach wie möglich gehalten werden

Die oben genannten Faktoren liegen zufällig auch in der hierarchischen Reihenfolge, die zu befolgen versucht. Mit anderen Worten, es macht für mich wenig Sinn, 2, 3 oder 4 zu priorisieren, wenn 1 die Anforderungen nicht ganz erfüllt. Die Lesbarkeit steht für mich ganz unten auf der Liste, da ich in den meisten Fällen der Syntax folgen kann.

Erfassungsgruppen und Lookarounds wirken sich häufig auf die Mustereffizienz aus. Die Wahrheit ist, dass Sie nicht über die Effizienz hinarbeiten müssen, es sei denn, Sie führen diesen regulären Ausdruck für Tausende von Eingabezeichenfolgen aus. Es ist vielleicht wichtiger, sich auf die Lesbarkeit von Mustern zu konzentrieren, die mit der Kürze des Musters verbunden sein können.

Einige der folgenden Muster erfordern eine zusätzliche Behandlung / Kennzeichnung durch ihre preg_Funktion, aber hier sind einige Mustervergleiche, die auf der Beispieleingabe des OP basieren:

preg_split() Muster:

  • /^[^A-Z]+\K|[A-Z][^A-Z]+\K/ (21 Schritte)
  • /(^[^A-Z]+|[A-Z][^A-Z]+)/ (26 Schritte)
  • /[^A-Z]+\K(?=[A-Z])/ (43 Schritte)
  • /(?=[A-Z])/ (50 Schritte)
  • /(?=[A-Z]+)/ (50 Schritte)
  • /([a-z]{1})[A-Z]{1}/ (53 Schritte)
  • /([a-z0-9])([A-Z])/ (68 Schritte)
  • /(?<=[a-z])(?=[A-Z])/x (94 Schritte) ... für die Aufzeichnung ist das xnutzlos.
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/ (134 Schritte)

preg_match_all() Muster:

  • /[A-Z]?[a-z]+/ (14 Schritte)
  • /((?:^|[A-Z])[a-z]+)/ (35 Schritte)

Ich werde darauf hinweisen, dass es einen subtilen Unterschied zwischen der Ausgabe von preg_match_all()und gibt preg_split(). preg_match_all()gibt ein zweidimensionales Array aus, mit anderen Worten, alle Fullstring-Übereinstimmungen befinden sich im [0]Subarray; Wenn eine Erfassungsgruppe verwendet wird, befinden sich diese [1]Teilzeichenfolgen im Subarray. Andererseits gibt preg_split()nur ein eindimensionales Array aus und bietet daher einen weniger aufgeblähten und direkteren Pfad zur gewünschten Ausgabe.

Einige der Muster sind unzureichend, wenn es sich um camelCase-Zeichenfolgen handelt, die einen ALLCAPS / Akronym-Teilstring enthalten. Wenn dies ein Randfall ist, der in Ihrem Projekt möglich ist, ist es logisch, nur Muster zu berücksichtigen, die diese Fälle korrekt behandeln. Ich werde keine TitleCase-Eingabezeichenfolgen testen, da dies zu weit von der Frage entfernt ist.

Neue erweiterte Batterie von Teststrings:

oneTwoThreeFour
hasConsecutiveCAPS
newNASAModule
USAIsGreatAgain 

Geeignete preg_split()Muster:

  • /[a-z]+\K|(?=[A-Z][a-z]+)/(149 Schritte) * Ich musste verwenden, [a-z]damit die Demo richtig zählt
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/ (547 Schritte)

Geeignetes preg_match_all()Muster:

  • /[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|$)/ (75 Schritte)

Schließlich meine Empfehlungen basierend auf meinen Musterprinzipien / Faktorhierarchie. I auch empfehlen preg_split()über preg_match_all()(trotz der Muster weniger Schritten) als eine Angelegenheit von Unmittelbarkeit auf die gewünschte Ausgabestruktur. (Natürlich wählen Sie, was Sie möchten)

Code: ( Demo )

$noAcronyms = 'oneTwoThreeFour';
var_export(preg_split('~^[^A-Z]+\K|[A-Z][^A-Z]+\K~', $noAcronyms, 0, PREG_SPLIT_NO_EMPTY));
echo "\n---\n";
var_export(preg_match_all('~[A-Z]?[^A-Z]+~', $noAcronyms, $out) ? $out[0] : []);

Code: ( Demo )

$withAcronyms = 'newNASAModule';
var_export(preg_split('~[^A-Z]+\K|(?=[A-Z][^A-Z]+)~', $withAcronyms, 0, PREG_SPLIT_NO_EMPTY));
echo "\n---\n";
var_export(preg_match_all('~[A-Z]?[^A-Z]+|[A-Z]+(?=[A-Z][^A-Z]|$)~', $withAcronyms, $out) ? $out[0] : []);

2

Ich habe den Code von Cool Ridgerunner (oben) genommen und daraus eine Funktion gemacht:

echo deliciousCamelcase('NewNASAModule');

function deliciousCamelcase($str)
{
    $formattedStr = '';
    $re = '/
          (?<=[a-z])
          (?=[A-Z])
        | (?<=[A-Z])
          (?=[A-Z][a-z])
        /x';
    $a = preg_split($re, $str);
    $formattedStr = implode(' ', $a);
    return $formattedStr;
}

Dies wird zurückkehren: New NASA Module


1

Eine andere Option ist das Matching. /[A-Z]?[a-z]+/Wenn Sie wissen, dass Ihre Eingabe das richtige Format hat, sollte sie gut funktionieren.

[A-Z]?würde mit einem Großbuchstaben (oder nichts) übereinstimmen. [a-z]+würde dann alle folgenden Kleinbuchstaben bis zur nächsten Übereinstimmung abgleichen.

Arbeitsbeispiel: https://regex101.com/r/kNZfEI/1


Schön schlank - immer lieber so.
Benjamin

@jbobbins - Danke, aktualisiert. ideone hat irgendwann alte beispiele abgelaufen, so viele alte beispiele sind immer noch kaputt.
Kobi

@Kobi danke. Nur damit Sie sich bewusst sind, habe ich den Bestätigungstext aus dem Beitrag von rr- eingefügt, und diejenigen mit mehreren Großbuchstaben zusammen funktionieren nicht. regex101.com/r/kNZfEI/2
jbobbins

0

Sie können auf einem "Gleiten" von Kleinbuchstaben zu Großbuchstaben auf folgende Weise aufteilen:

$parts = preg_split('/([a-z]{1})[A-Z]{1}/', $string, -1, PREG_SPLIT_DELIM_CAPTURE);        
//PREG_SPLIT_DELIM_CAPTURE to also return bracketed things
var_dump($parts);

Ärgerlicherweise müssen Sie dann die Wörter aus jedem entsprechenden Elementpaar in $ parts neu erstellen

Hoffe das hilft


Hoppla, dies wird wahrscheinlich in der Ausgabe von CONSECUTIVE CAPS fehlschlagen
Daniel Rhodes

0

Zunächst einmal Codaddict, danke für dein Muster, es hat sehr geholfen!

Ich brauchte eine Lösung, die funktioniert, falls eine Präposition 'a' existiert:

zB thisIsACamelcaseSentence.

Ich fand die Lösung in einem zweistufigen preg_match und erstellte eine Funktion mit einigen Optionen:

/*
 * input: 'thisIsACamelCaseSentence' output: 'This Is A Camel Case Sentence'
 * options $case: 'allUppercase'[default] >> 'This Is A Camel Case Sentence'
 *                'allLowerCase'          >> 'this is a camel case sentence'
 *                'firstUpperCase'        >> 'This is a camel case sentence'
 * @return: string
 */

function camelCaseToWords($string, $case = null){
    isset($case) ? $case = $case : $case = 'allUpperCase';

    // Find first occurances of two capitals
    preg_match_all('/((?:^|[A-Z])[A-Z]{1})/',$string, $twoCapitals);

    // Split them with the 'zzzzzz' string. e.g. 'AZ' turns into 'AzzzzzzZ'
    foreach($twoCapitals[0] as $match){
        $firstCapital = $match[0];
        $lastCapital = $match[1];
        $temp = $firstCapital.'zzzzzz'.$lastCapital;
        $string = str_replace($match, $temp, $string);  
    }

    // Now split words
    preg_match_all('/((?:^|[A-Z])[a-z]+)/', $string, $words);

    $output = "";
    $i = 0;
    foreach($words[0] as $word){

            switch($case){
                case 'allUpperCase':
                $word = ucfirst($word);
                break;

                case 'allLowerCase': 
                $word = strtolower($word);
                break;

                case 'firstUpperCase':
                ($i == 0) ? $word = ucfirst($word) : $word = strtolower($word);
                break;                  
            }

            // remove te 'zzzzzz' from a word if it has
            $word = str_replace('zzzzzz','', $word);    
            $output .= $word." ";
            $i++;
    }
    return $output; 
}

Fühlen Sie sich frei, es zu benutzen, und falls es einen "einfacheren" Weg gibt, dies in einem Schritt zu tun, kommentieren Sie bitte!


0

Volle Funktion basierend auf @codaddict Antwort:

function splitCamelCase($str) {
    $splitCamelArray = preg_split('/(?=[A-Z])/', $str);

    return ucwords(implode($splitCamelArray, ' '));
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.