web-dev-qa-db-ja.com

php preg_match(正規表現)を使用してcamelCase Wordを単語に分割する

Wordを分割するにはどうすればよいですか:

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

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.
75
codaddict

preg_splitは次のように使用できます。

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

それを見る

基本的には、入力文字列を大文字の直前で分割します。使用される正規表現(?=[A-Z])は、大文字の直前のポイントと一致します。

69
codaddict

私はこれが受け入れられた答えを持つ古い質問であることを知っていますが、私見ではより良い解決策があります:

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

この正規表現(codaddictの'/(?=[A-Z])/'ソリューション-整形式のキャメルケースの単語の魅力のように機能する)は、position文字列内でテキストをまったく消費しません。このソリューションには、StartsWithCaphasConsecutiveCAPSなどのあまり整形式ではない擬似キャメルケースの単語に対しても正しく機能するという追加の利点があります。

入力:

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のコメントへの応答)。

50
ridgerunner

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

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']);
11
rr-
$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」として一致させます。次に、最初のWordの大文字を小文字にするか、スペースで展開するか、_または他の文字を使用してそれらを分離します。

短くてシンプル。

6
ArtisticPheonix

かっこいい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

2
Jarrod

プロジェクトに最適なパターンを決定する際には、次のパターン要素を考慮する必要があります。

  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ステップ)

preg_match_all()preg_split()の出力には微妙な違いがあることを指摘します。 preg_match_all()は2次元配列を出力します。つまり、すべての文字列の一致は_[0]_サブ配列になります。使用されるキャプチャグループがある場合、それらのサブストリングは_[1]_サブ配列になります。一方、preg_split()は1次元配列のみを出力するため、肥大化が少なく、目的の出力へのより直接的なパスを提供します。

ALLCAPS/acronym substringを含むcamelCase文字列を処理する場合、一部のパターンは不十分です。これがプロジェクト内で可能なフリンジケースである場合、これらのケースを正しく処理するパターンのみを考慮するのが論理的です。 TitleCaseの入力文字列をテストするつもりはありません。これは、質問からあまりにも遠いためです。

テスト文字列の新しい拡張バッテリー:

_oneTwoThreeFour
hasConsecutiveCAPS
newNASAModule
USAIsGreatAgain 
_

適切なpreg_split()パターン:

  • /[a-z]+\K|(?=[A-Z][a-z]+)/(149ステップ)*デモを正しくカウントするには_[a-z]_を使用する必要がありました
  • /(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/(547ステップ)

適切なpreg_match_all()パターン:

  • /[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|$)/(75ステップ)

最後に、パターンの原則/要因の階層に基づいた推奨事項。また、必要な出力構造への直接性の問題として、preg_split() over preg_match_all()(パターンのステップ数が少ないにもかかわらず)をお勧めします。 (もちろん、好きなものを選択してください)

コード:( デモ

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

コード:( デモ

_$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

別のオプションは/[A-Z]?[a-z]+/-入力が正しい形式であることがわかっている場合、うまく機能するはずです。

[A-Z]?は大文字と一致します(または何も一致しません)。 [a-z]+は、次の一致まで、後続のすべての小文字と一致します。

作業例: https://regex101.com/r/kNZfEI/1

1
Kobi

@codaddictの回答に基づく全機能:

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

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

たぶん私の質問はあなたを助けることができる、私は昨日同じことを尋ねたが、Javaについて

文字列を大文字の文字に分割する

0
pringlesinn

次のように、「グライド」で小文字から大文字に分割できます。

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

面倒なことに、$ partsのアイテムの対応する各ペアから単語を再構築する必要があります

お役に立てれば

0
Daniel Rhodes

まず、codaddictがあなたのパターンに感謝します。

前置詞 'a'が存在する場合に機能するソリューションが必要でした。

例えばthisIsACamelcaseSentence。

私は2ステップのpreg_matchを行うことで解決策を見つけ、いくつかのオプションを使用して関数を作成しました:

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

気軽に使用してください。1ステップでこれを行う「簡単な」方法がある場合はコメントしてください。

0
joronimo