PHP分解字符串,但将引号中的单词视为单个单词

Posted

技术标签:

【中文标题】PHP分解字符串,但将引号中的单词视为单个单词【英文标题】:PHP explode the string, but treat words in quotes as a single word 【发布时间】:2011-01-13 05:33:46 【问题描述】:

如何分解以下字符串:

Lorem ipsum "dolor sit amet" consectetur "adipiscing elit" dolor

进入

array("Lorem", "ipsum", "dolor sit amet", "consectetur", "adipiscing elit", "dolor")

使引用中的文字被视为一个单词。

这是我现在拥有的:

$mytext = "Lorem ipsum %22dolor sit amet%22 consectetur %22adipiscing elit%22 dolor"
$noquotes = str_replace("%22", "", $mytext");
$newarray = explode(" ", $noquotes);

但我的代码将每个单词分成一个数组。如何使引号内的单词视为一个单词?

【问题讨论】:

这听起来像是正则表达式的工作 另见An explode() function that ignores characters inside quotes? 【参考方案1】:

你可以使用preg_match_all(...):

$text = 'Lorem ipsum "dolor sit amet" consectetur "adipiscing \\"elit" dolor';
preg_match_all('/"(?:\\\\.|[^\\\\"])*"|\S+/', $text, $matches);
print_r($matches);

这将产生:

Array
(
    [0] => Array
        (
            [0] => Lorem
            [1] => ipsum
            [2] => "dolor sit amet"
            [3] => consectetur
            [4] => "adipiscing \"elit"
            [5] => dolor
        )

)

如您所见,它还解释了带引号的字符串中的转义引号。

编辑

简短说明:

"           # match the character '"'
(?:         # start non-capture group 1 
  \\        #   match the character '\'
  .         #   match any character except line breaks
  |         #   OR
  [^\\"]    #   match any character except '\' and '"'
)*          # end non-capture group 1 and repeat it zero or more times
"           # match the character '"'
|           # OR
\S+         # match a non-whitespace character: [^\s] and repeat it one or more times

如果匹配 %22 而不是双引号,你会这样做:

preg_match_all('/%22(?:\\\\.|(?!%22).)*%22|\S+/', $text, $matches);

【讨论】:

是否有理由不使用preg_split 而不是preg_match_all?它似乎更适合 IMO。 太棒了!我必须研究一下代码才能弄清楚刚刚发生了什么!谢谢 @prodigitalson:不,使用preg_split(...) 你不能解释转义字符。 preg_match_all(...)“行为”更像是一个解析器,这是更自然的事情。此外,使用preg_split(...),您需要提前查看每个空格以查看其前面有多少引号,使其成为O(n^2) 操作:对于小字符串没有问题,但当字符串较大时可能会减少运行时间参与其中。 @timofey,看看我的编辑。如果您不清楚,请随时要求更多说明:您是维护代码的人,所以您应该理解它(如果需要,我很乐意提供额外信息)。 感谢 Bart K.!我已经在谷歌上搜索那个答案了:)【参考方案2】:

使用str_getcsv() 会容易得多。

$test = 'Lorem ipsum "dolor sit amet" consectetur "adipiscing elit" dolor';
var_dump(str_getcsv($test, ' '));

给你

array(6) 
  [0]=>
  string(5) "Lorem"
  [1]=>
  string(5) "ipsum"
  [2]=>
  string(14) "dolor sit amet"
  [3]=>
  string(11) "consectetur"
  [4]=>
  string(15) "adipiscing elit"
  [5]=>
  string(5) "dolor"

【讨论】:

这适用于我的开发机器,但不适用于我的生产服务器。 :-/ str_getcsv 需要 php 5.3。 请注意它会“忽略”引号。如果你也需要他们在分裂中,那么这也行不通。 我进行了一些速度测试,preg_match_all 大约快 3-5 倍。对于大多数人来说可能不是问题,特别是如果不需要引号(在这种情况下它更容易使用),但我认为值得一提。 @err 愿意分享你的测试吗?【参考方案3】:

你也可以试试这个多重爆炸功能

function multiexplode ($delimiters,$string)


$ready = str_replace($delimiters, $delimiters[0], $string);
$launch = explode($delimiters[0], $ready);
return  $launch;


$text = "here is a sample: this text, and this will be exploded. this also | this one too :)";
$exploded = multiexplode(array(",",".","|",":"),$text);

print_r($exploded);

【讨论】:

这个答案很好,但是如果你要求它分割空格和引号,它会分割引号内的空格。【参考方案4】:

我来到这里时遇到了与此类似的复杂字符串拆分问题,但这里没有一个答案完全符合我的要求 - 所以我自己写了。

我在这里发布它以防万一它对其他人有帮助。

这可能是一种非常缓慢且低效的方法 - 但它对我有用。

function explode_adv($openers, $closers, $togglers, $delimiters, $str)

    $chars = str_split($str);
    $parts = [];
    $nextpart = "";
    $toggle_states = array_fill_keys($togglers, false); // true = now inside, false = now outside
    $depth = 0;
    foreach($chars as $char)
    
        if(in_array($char, $openers))
            $depth++;
        elseif(in_array($char, $closers))
            $depth--;
        elseif(in_array($char, $togglers))
        
            if($toggle_states[$char])
                $depth--; // we are inside a toggle block, leave it and decrease the depth
            else
                // we are outside a toggle block, enter it and increase the depth
                $depth++;

            // invert the toggle block state
            $toggle_states[$char] = !$toggle_states[$char];
        
        else
            $nextpart .= $char;

        if($depth < 0) $depth = 0;

        if(in_array($char, $delimiters) &&
           $depth == 0 &&
           !in_array($char, $closers))
        
            $parts[] = substr($nextpart, 0, -1);
            $nextpart = "";
        
    
    if(strlen($nextpart) > 0)
        $parts[] = $nextpart;

    return $parts;

用法如下。 explode_adv 接受 5 个参数:

    打开块的字符数组 - 例如[(等 关闭块的字符数组 - 例如])等 切换块的字符数组 - 例如"'等 应导致拆分为下一部分的字符数组。 要处理的字符串。

此方法可能存在缺陷 - 欢迎修改。

【讨论】:

【参考方案5】:

在某些情况下,鲜为人知的token_get_all() 可能很有用:

$tokens = token_get_all("<?php $text ?>");
$separator = ' ';
$items = array();
$item = "";
$last = count($tokens) - 1;
foreach($tokens as $index => $token) 
    if($index != 0 && $index != $last) 
        if(count($token) == 3) 
            if($token[0] == T_CONSTANT_ENCAPSED_STRING) 
                $token = substr($token[1], 1, -1);
             else 
                $token = $token[1];
            
        
        if($token == $separator) 
            $items[] = $item;
            $item = "";
         else 
            $item .= $token;
        
    

结果:

Array
(
    [0] => Lorem
    [1] => ipsum
    [2] => dolor sit amet
    [3] => consectetur
    [4] => adipiscing elit
    [5] => dolor
)

【讨论】:

以上是关于PHP分解字符串,但将引号中的单词视为单个单词的主要内容,如果未能解决你的问题,请参考以下文章

我如何每行读取一个文本文件,然后将字符串分解成单个单词(分成一个树集)而不重复?

如何使用 Javascript 分解 HTML 字符串中的每个单词,然后在 HTML 中一个一个地显示每个单词? [复制]

如何从一行中删除某些单词,但将其余部分放入带有批处理的字符串/变量中?

搜索字符串中的单个单词

通过索引号调用字符串中的特定单词?

导入文本查询字母单词个数