Program Club

CamelCase 단어를 PHP preg_match (정규식)를 사용하여 단어로 분할

proclub 2020. 11. 25. 21:25
반응형

CamelCase 단어를 PHP preg_match (정규식)를 사용하여 단어로 분할


단어를 나누는 방법은 다음과 같습니다.

oneTwoThreeFour

내가 얻을 수 있도록 배열에 :

one Two Three Four

preg_match?

지 쳤지 만 단어 전체를

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

다음 preg_match_all과 같이 사용할 수도 있습니다 .

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

설명:

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

다음 preg_split과 같이 사용할 수 있습니다 .

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

보기

기본적으로 대문자 바로 전에 입력 문자열을 분할하고 있습니다. 사용 된 정규식 (?=[A-Z])은 대문자 바로 앞의 지점과 일치합니다.


나는 이것이 받아 들여지는 대답이있는 오래된 질문이라는 것을 알고 있지만 IMHO에는 더 나은 해결책이 있습니다.

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

이 정규식은 ( '/(?=[A-Z])/'잘 형성된 camelCase 단어의 매력처럼 작동하는 codaddict의 솔루션과 같이) 문자열 내의 위치 만 일치 하고 텍스트를 전혀 사용하지 않습니다. 이 솔루션은 다음과 같이 잘 구성되지 않은 유사 낙타 단어에도 올바르게 작동한다는 추가 이점이 있습니다. StartsWithCap및 : hasConsecutiveCAPS.

입력:

oneTwoThreeFour
StartsWithCap
hasConsecutiveCAPS
NewNASAModule

산출:

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"

수정 됨 : 2014-04-12 : 정규식, 스크립트 및 테스트 데이터를 수정하여 올바르게 분할 : "NewNASAModule"케이스 (rr의 의견에 대한 응답).


@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, " " );
}

ridgerunner의 대답은 훌륭하게 작동하지만 문장 중간에 나타나는 전체 대문자 하위 문자열에는 작동하지 않는 것 같습니다. 나는 다음을 사용하고 이것들을 잘 다루는 것 같습니다.

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

일부 테스트 사례 :

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

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

트릭은 반복 가능한 패턴입니다 $ 1 $ 2 $ 1 $ 2 이하 UPPERlower UPPERlower 등 ... 예를 들어 helloWorld = $ 1은 "hello"와 일치하고 $ 2는 "W"와 일치하고 $ 1은 "orld"와 다시 일치하므로 간단히 $ 1 $ 2 $ 1 또는 "hello World"는 HelloWorld를 $ 2 $ 1 $ 2 $ 1 또는 다시 "Hello World"와 일치시킵니다. 그런 다음 첫 번째 단어를 대문자로 소문자로 바꾸거나 공백에서 폭발 시키거나 _ 또는 다른 문자를 사용하여 분리 할 수 ​​있습니다.

짧고 간단합니다.


나는 멋진 사람 Ridgerunner의 코드 (위)를 가져 와서 함수로 만들었다.

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

다음을 반환합니다. New NASA Module


또 다른 옵션은 매칭입니다. /[A-Z]?[a-z]+/입력 한 형식이 올바른지 알고 있다면 잘 작동합니다.

[A-Z]?대문자 (또는 아무것도 일치하지 않음)와 일치합니다. [a-z]+그런 다음 다음 일치까지 다음 모든 소문자와 일치합니다.

실례 : https://regex101.com/r/kNZfEI/1


프로젝트에 가장 적합한 패턴을 결정할 때 다음 패턴 요소를 고려해야합니다.

  1. 정확성 (견고성)-패턴이 모든 경우에 정확하고 합리적으로 미래 보장형인지 여부
  2. 효율성-패턴은 직접적이고 신중해야하며 불필요한 노동을 피해야합니다.
  3. 간결함-패턴은 불필요한 문자 길이를 피하기 위해 적절한 기술을 사용해야합니다.
  4. 가독성-패턴은 가능한 한 단순해야합니다.

위의 요인들은 또한 순종하려고 노력하는 계층 적 순서에 있습니다. 즉, 1이 요구 사항을 완전히 충족하지 못할 때 2, 3 또는 4의 우선 순위를 지정하는 것은 나에게별로 의미가 없습니다. 대부분의 경우 구문을 따를 수 있기 때문에 가독성이 목록 맨 아래에 있습니다.

캡처 그룹 및 룩 어라운드는 종종 패턴 효율성에 영향을 미칩니다. 진실은 수천 개의 입력 문자열에 대해이 정규식을 실행하지 않는 한 효율성에 대해 노력할 필요가 없다는 것입니다. 패턴 간결성과 연관 될 수있는 패턴 가독성에 초점을 맞추는 것이 더 중요 할 수 있습니다.

아래의 일부 패턴은 preg_기능별로 추가 처리 / 플래 깅이 필요 하지만 다음은 OP의 샘플 입력을 기반으로 한 패턴 비교입니다.

preg_split() 패턴 :

  • /^[^A-Z]+\K|[A-Z][^A-Z]+\K/ (21 단계)
  • /(^[^A-Z]+|[A-Z][^A-Z]+)/ (26 단계)
  • /[^A-Z]+\K(?=[A-Z])/ (43 단)
  • /(?=[A-Z])/ (50 단계)
  • /(?=[A-Z]+)/ (50 단계)
  • /([a-z]{1})[A-Z]{1}/ (53 단)
  • /([a-z0-9])([A-Z])/ (68 단)
  • /(?<=[a-z])(?=[A-Z])/x(94 단계) ... 기록을 위해 x쓸모가 없습니다.
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/ (134 단)

preg_match_all() 패턴 :

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

I'll point out that there is a subtle difference between the output of preg_match_all() and preg_split(). preg_match_all() will output a 2-dimensional array, in other words, all of the fullstring matches will be in the [0] subarray; if there is a capture group used, those substrings will be in the [1] subarray. On the other hand, preg_split() only outputs a 1-dimensional array and therefore provides a less bloated and more direct path to the desired output.

Some of the patterns are insufficient when dealing with camelCase strings that contain an ALLCAPS/acronym substring in them. If this is a fringe case that is possible within your project, it is logical to only consider patterns that handle these cases correctly. I will not be testing TitleCase input strings because that is creeping too far from the question.

New Extended Battery of Test Strings:

oneTwoThreeFour
hasConsecutiveCAPS
newNASAModule
USAIsGreatAgain 

Suitable preg_split() patterns:

  • /[a-z]+\K|(?=[A-Z][a-z]+)/ (149 steps) *I had to use [a-z] for the demo to count properly
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/ (547 steps)

Suitable preg_match_all() pattern:

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

Finally, my recommendations based on my pattern principles / factor hierarchy. Also, I recommend preg_split() over preg_match_all() (despite the patterns having less steps) as a matter of directness to the desired output structure. (of course, choose whatever you like)

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

You can split on a "glide" from lowercase to uppercase thus:

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

Annoyingly you will then have to rebuild the words from each corresponding pair of items in $parts

Hope this helps


First of all codaddict thank you for your pattern, it helped a lot!

I needed a solution that works in case a preposition 'a' exists:

e.g. thisIsACamelcaseSentence.

I found the solution in doing a two step preg_match and made a function with some 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; 
}

Feel free to use it, and in case there is an 'easier' way to do this in one step please comment!


Full function based on @codaddict answer:

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

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

참고URL : https://stackoverflow.com/questions/4519739/split-camelcase-word-into-words-with-php-preg-match-regular-expression

반응형