web-dev-qa-db-fra.com

Fractionner camelCase Word en mots avec php preg_match (Expression régulière)

Comment pourrais-je diviser la Parole en deux:

oneTwoThreeFour

dans un tableau pour que je puisse obtenir:

one Two Three Four

avec preg_match?

J'en ai marre de ça mais ça donne juste la Parole entière

$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)`;
59
Good-bye

Vous pouvez également utiliser preg_match_all comme:

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

Explication:

(        - 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.
74
codaddict

Vous pouvez utiliser preg_split comme:

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

Voir le

Je divise la chaîne d'entrée juste avant la lettre majuscule. Le regex utilisé (?=[A-Z]) correspond au point juste avant une lettre majuscule.

65
codaddict

Je sais que c'est une vieille question avec une réponse acceptée, mais à mon humble avis, il existe une meilleure solution:

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

Notez que cette expression rationnelle (comme la solution '/(?=[A-Z])/' de codaddict - qui fonctionne comme un charme pour les mots camelCase bien formés), correspond uniquement à un position dans la chaîne et ne consomme aucun texte. Cette solution présente également l’avantage supplémentaire de fonctionner correctement pour des mots pseudo-camelcase de moindre qualité, tels que: StartsWithCap et: hasConsecutiveCAPS.

Contribution:

oneTwoThreeFour
StartsWithCap
hasConsecutiveCAPS
NewNASAModule 

Sortie:

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" 

Modifié le: 2014-04-12: Modification des expressions rationnelles, des scripts et des données de test pour scinder correctement: "NewNASAModule" case (en réponse au commentaire de rr).

49
ridgerunner

Une version fonctionnelle de la réponse de @ ridgerunner.

/**
 * 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, " " );
}
11
blak3r

Bien que la réponse de ridgerunner fonctionne à merveille, elle ne semble pas fonctionner avec les sous-chaînes en majuscules qui apparaissent au milieu de la phrase. J'utilise la suite et il semble traiter ces derniers très bien:

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

Quelques cas de test:

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']);
10
rr-
$string = preg_replace( '/([a-z0-9])([A-Z])/', "$1 $2", $string );

Le tour est un motif répétable $ 1 $ 2 $ 1 $ 2 $ ou moins UPPERlower UPPERlower etc .... Par exemple HelloWorld = $ 1 correspond à "bonjour", $ 2 correspond à "W" et $ 1 correspond à "orld" à nouveau vous obtenez 1 $ 2 $ 1 $ ou "hello World", correspond à HelloWorld en tant que 2 $ US 1 $ 2 $ 1 $ ou encore "Hello World". Ensuite, vous pouvez les mettre en minuscule, mettre le premier mot en majuscule ou les faire exploser sur l’espace, ou utiliser un _ ou un autre caractère pour les séparer.

Court et simple.

5
ArtisticPheonix

J'ai pris le code de Ridgerunner (ci-dessus) et l'ai transformé en une fonction:

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

Cela retournera: New NASA Module

2
Jarrod

Pour déterminer le modèle optimal pour votre projet, vous devez prendre en compte les facteurs de modèle suivants:

  1. Précision (robustesse) - Indique si le motif est correct dans tous les cas et s’il est raisonnablement à l’avenir
  2. Efficacité - le modèle doit être direct, délibéré et éviter le travail inutile
  3. Brièveté - le motif doit utiliser des techniques appropriées pour éviter les longueurs de caractères inutiles
  4. Lisibilité - le modèle doit être aussi simple que possible

Les facteurs ci-dessus se trouvent également dans l'ordre hiérarchique qui s'efforce d'obéir. En d'autres termes, il n'a pas beaucoup de sens pour moi de donner la priorité à 2, 3 ou 4 lorsque 1 ne satisfait pas tout à fait aux exigences. La lisibilité est au bas de la liste pour moi parce que dans la plupart des cas, je peux suivre la syntaxe.

Les groupes de capture et les ressemblances ont souvent une incidence sur l'efficacité des modèles. La vérité est que, à moins d'exécuter cette expression rationnelle sur des milliers de chaînes d'entrée, il n'est pas nécessaire de rechercher l'efficacité. Il est peut-être plus important de se concentrer sur la lisibilité des modèles, qui peut être associée à la brièveté des modèles.

Certains modèles ci-dessous nécessiteront des manipulations/marquages ​​supplémentaires de la part de leur fonction preg_, mais voici quelques comparaisons de modèles basées sur l'exemple de l'entrée du PO:

preg_split() patterns:

  • /^[^A-Z]+\K|[A-Z][^A-Z]+\K/ (21 étapes)
  • /(^[^A-Z]+|[A-Z][^A-Z]+)/ (26 étapes)
  • /[^A-Z]+\K(?=[A-Z])/ (43 étapes)
  • /(?=[A-Z])/ (50 étapes)
  • /(?=[A-Z]+)/ (50 étapes)
  • /([a-z]{1})[A-Z]{1}/ (53 étapes)
  • /([a-z0-9])([A-Z])/ (68 steps)
  • /(?<=[a-z])(?=[A-Z])/x (94 steps) ... pour mémoire, la x est inutile.
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/ (134 étapes)

preg_match_all() patterns:

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

Je vous ferai remarquer qu'il existe une différence subtile entre les résultats de preg_match_all() et preg_split(). preg_match_all() générera un tableau à 2 dimensions. En d'autres termes, toutes les correspondances de chaîne complète seront dans la sous-matrice [0]; si un groupe de capture est utilisé, ces sous-chaînes seront dans la sous-matrice [1]. Par ailleurs, preg_split() ne génère qu'un tableau à une dimension et fournit donc un chemin moins chargé et plus direct vers la sortie souhaitée.

Certains des modèles sont insuffisants lorsqu'il s'agit de chaînes camelCase contenant une sous-chaîne ALLCAPS/acronyme. S'il s'agit d'un cas marginal qui est possible dans votre projet, il est logique de ne considérer que les modèles qui gèrent ces cas correctement. Je ne vais pas tester les chaînes d’entrée TitleCase car cela s’éloigne trop de la question.

Nouvelle batterie étendue de chaînes de test:

oneTwoThreeFour
hasConsecutiveCAPS
newNASAModule
USAIsGreatAgain 

Modèles preg_split() appropriés:

  • /[a-z]+\K|(?=[A-Z][a-z]+)/ (149 étapes) * J'ai dû utiliser [a-z] pour que la démo compte correctement
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/ (547 étapes)

Modèle preg_match_all() approprié:

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

Enfin, mes recommandations reposent sur mes principes de structure/hiérarchie de facteurs. De plus, je recommande preg_split() à preg_match_all() (malgré les motifs comportant moins d'étapes) par souci de cohérence avec la structure de sortie souhaitée. (bien sûr, choisissez ce que vous voulez)

Code: ( Démo )

$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: ( Démo )

$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] : []);
1
mickmackusa

Faire correspondre /[A-Z]?[a-z]+/ est une autre option. Si vous savez que votre saisie est au bon format, elle devrait fonctionner correctement.

[A-Z]? correspondrait à une lettre majuscule (ou rien). [a-z]+ devrait alors correspondre à toutes les lettres minuscules suivantes, jusqu'à la prochaine correspondance.

Exemple de travail: https://regex101.com/r/kNZfEI/1

1
Kobi

Fonction complète basée sur la réponse @codaddict:

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

    return ucwords(implode($splitCamelArray, ' '));
}
0
guizo

Peut-être que ma question peut vous aider. J'ai demandé la même chose hier, mais à propos de Java

Casser des chaînes en caractères majuscules

0
pringlesinn

Vous pouvez diviser en "glisse" de minuscule en majuscule ainsi:

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

De manière ennuyeuse, vous devrez alors reconstruire les mots de chaque paire d’éléments correspondants dans $ pièces

J'espère que cela t'aides

0
Daniel Rhodes

Tout d'abord, merci codaddict pour votre modèle, cela a beaucoup aidé!

J'avais besoin d'une solution qui fonctionne dans le cas où une préposition "a" existe:

par exemple. thisIsACamelcaseSentence.

J'ai trouvé la solution en effectuant un preg_match en deux étapes et créé une fonction avec quelques options: 

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

N'hésitez pas à l'utiliser, et dans le cas où il existe un moyen «plus facile» de le faire en une étape, veuillez commenter!

0
joronimo