【问题标题】:Split camelCase word into words with php preg_match (Regular Expression)使用php preg_match(正则表达式)将camelCase单词拆分为单词
【发布时间】:2011-05-30 00:24:37
【问题描述】:

我将如何拆分单词:

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

【问题讨论】:

标签: php regex string preg-match


【解决方案1】:

您可以将preg_split 用作:

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

See it

我基本上是在大写字母之前分割输入字符串。使用的正则表达式 (?=[A-Z]) 匹配大写字母之前的点。

【讨论】:

    【解决方案2】:

    您也可以将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.
    

    【讨论】:

    • 非捕获组不会导致结果为[one, wo, hree, our]?
    • @AaronJLang 否,因为外括号捕获整个组,包括子组。这是一个他不想弄乱 $matches 集合的子组。
    • 这对我来说失败了,“TestID”使用:“preg_match_all('/((?:^|[AZ])[az]+)/', $key, $matches); die(内爆(' ', $matches[0]));"因为它不喜欢连续大写的问题。我需要用空格分隔大小写更改,@blak3r 的解决方案对我有用:stackoverflow.com/a/17122207/539149
    • HTMLParser 等字符串的更好解决方案:stackoverflow.com/a/6572999/1697320
    • 按照@TarranJones 的规定(虽然没有说得太清楚),你不需要外括号。 '/(?:^|[A-Z])[a-z]+/' 的匹配字符串足以生成一个数组(而不是两个)。这是因为preg_match_all() 会自动捕获匹配的所有实例,而无需您具体规定。
    【解决方案3】:

    我知道这是一个已被接受的老问题,但恕我直言,有更好的解决方案:

    <?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 并且根本不使用任何文本。此解决方案还有一个额外的好处,即它也适用于格式不太好的伪驼峰式单词,例如: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 的评论)。

    【讨论】:

    • 这是一个更好的解决方案,第一次工作(其他人在数组中添加了空白值,这个是完美的!谢谢!+1
    • NewNASAModule 之类的字符串似乎有问题(输出:[New, NASAModule];我希望[New, NASA, Module]
    • @rr - 是的,你是对的。请参阅我的另一个更新的答案:NewNASAModule 正确:RegEx to split camelCase or TitleCase (advanced)
    • 它不包括数字的情况。出于某种原因,其他回复者也忽略了这个基本事实。例如。 “Css3Transform”或类似的
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:
        $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”。然后您可以将它们小写,将第一个单词大写或在空格上展开,或使用 _ 或其他字符将它们分开。

        简短而简单。

        【讨论】:

          【解决方案7】:

          在为您的项目确定最佳模式时,您需要考虑以下模式因素:

          1. 准确性(稳健性)- 模式是否在所有情况下都是正确的,并且是否合理地面向未来
          2. 效率——模式应该是直接的、深思熟虑的,避免不必要的劳动
          3. 简洁——模式应该使用适当的技术来避免不必要的字符长度
          4. 可读性——模式应尽可能简单

          以上因素也恰好是在努力服从的等级秩序中。换句话说,当 1 不能完全满足要求时,优先考虑 2、3 或 4 对我来说没有多大意义。对我来说,可读性位于列表的底部,因为在大多数情况下我可以遵循语法。

          捕获组和 Lookarounds 通常会影响模式效率。事实是,除非您在数千个输入字符串上执行此正则表达式,否则无需为效率而努力。可能更重要的是关注与模式简洁相关的模式可读性。

          下面的一些模式需要通过它们的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 步)
          • /(?&lt;=[a-z])(?=[A-Z])/x(94 步)...作为记录,x 没用。
          • /(?&lt;=[a-z])(?=[A-Z])|(?&lt;=[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() 将输出一个二维数组,换句话说,所有的全字符串匹配都将在[0] 子数组中;如果使用了捕获组,则这些子字符串将位于 [1] 子数组中。另一方面,preg_split() 只输出一个一维数组,因此提供了一个不那么臃肿且更直接的路径到所需的输出。

          在处理其中包含全大写/首字母缩略词子字符串的驼峰式字符串时,某些模式是不够的。如果这是您项目中可能出现的边缘情况,则只考虑正确处理这些情况的模式是合乎逻辑的。我不会测试 TitleCase 输入字符串,因为这离问题太远了。

          新的扩展测试串电池:

          oneTwoThreeFour
          hasConsecutiveCAPS
          newNASAModule
          USAIsGreatAgain 
          

          合适的preg_split()模式:

          • /[a-z]+\K|(?=[A-Z][a-z]+)/(149 步)*我必须使用 [a-z] 才能让演示正确计数
          • /(?&lt;=[a-z])(?=[A-Z])|(?&lt;=[A-Z])(?=[A-Z][a-z])/(547 步)

          适合preg_match_all()模式:

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

          最后,我的建议基于我的模式原则/因素层次结构。此外,我推荐preg_split() 而不是preg_match_all()(尽管模式的步骤更少)作为对所需输出结构的直接性问题。 (当然,选择你喜欢的)

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

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

          【讨论】:

            【解决方案8】:

            我把酷哥 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

            【讨论】:

              【解决方案9】:

              另一个选项是匹配/[A-Z]?[a-z]+/ - 如果您知道您的输入格式正确,它应该可以正常工作。

              [A-Z]? 将匹配大写字母(或不匹配)。然后[a-z]+ 将匹配所有后面的小写字母,直到下一个匹配。

              工作示例:https://regex101.com/r/kNZfEI/1

              【讨论】:

              • 又好又瘦——总是喜欢这样。
              • @jbobbins - 谢谢,已更新。 ideone 在某些时候旧的例子过期了,所以很多旧的例子仍然被破坏。
              • @Kobi 谢谢。请注意,我粘贴了 rr- 帖子中的断言文本,而带有多个大写字母的断言文本不起作用。 regex101.com/r/kNZfEI/2
              【解决方案10】:

              您可以这样从小写“滑行”拆分为大写:

              $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 中每对相应的项目中重建单词

              希望对你有帮助

              【讨论】:

              • 哎呀,这可能会在 CONSECUTIVE CAPS 问题上失败
              【解决方案11】:

              首先 codaddict 感谢您的模式,它帮助很大!

              如果存在介词“a”,我需要一个可行的解决方案:

              例如thisIsACamelcaseSentence。

              我通过两步 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; 
              }
              

              随意使用它,如果有一种“更简单”的方法可以一步完成,请发表评论!

              【讨论】:

                【解决方案12】:

                基于@codaddict 答案的完整功能:

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2017-04-20
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-04-17
                  • 2012-09-24
                  相关资源
                  最近更新 更多