【问题标题】:How do I get data a regular expression does't match?如何获取正则表达式不匹配的数据?
【发布时间】:2012-03-03 21:22:30
【问题描述】:

我正在解析一条推文,作为向数据库添加提醒的快速方法。推文看起来像这样:

$tweet = '#notes @username !high_priority [Project Name] Tweet content';

我正在使用以下正则表达式来获取 #、@、!和[项目]

$match = preg_match_all('/(#\\w*[a-zA-Z_]+\\w*)|(!\\w*[a-zA-Z_]+\\w*)|(@\\w*[a-zA-Z_]+\\w*)|(\\[[^\\]]*\\])/i', 
    $tweet,
    $matches);

我还想知道如何获取剩余的“推文内容”,因此所有与正则表达式不匹配的内容都应保存到变量中。

此外,如果推文更像是,匹配顺序是否重要:

$tweet = '@username Tweet content [Project Name] #notes !high_priority';

有人知道怎么做吗?

【问题讨论】:

  • 您认为\w 会做什么?和[a-zA-Z]几乎一样
  • 只需遍历匹配项并从每个不以#、@、! 开头的匹配项组成一个字符串。 & [

标签: php regex parsing preg-match-all


【解决方案1】:

将正则表达式匹配的文本替换为空字符串。剩下的是正则表达式不匹配的内容。

【讨论】:

  • 我正在使用这个:$content = preg_replace('/(#\\w*[a-zA-Z_]+\\w*)|(!\\w*[a-zA-Z_]+\\w*)|(@\\w*[a-zA-Z_]+\\w*)|(\\[[^\\]]*\\])/i', '', $subject);,然后只是修剪剩余的空白。
【解决方案2】:

我没有测试过这段代码,但我认为这个非正则表达式的想法可能更适合你。本质上,您将字符串按空格拆分,然后解析每个部分。这种方法意味着部件的顺序无关紧要。

这有点棘手,因为内容和项目可以跨越多个部分,但我认为我的代码应该处理这个问题。它还假设每条推文只有一个标签、用户、项目和优先级。例如,如果会有多个主题标签,只需将它们放入数组而不是字符串中。最后,它没有任何错误处理来检测/防止奇怪的事情发生。

这是我未经测试的代码:

$data = array(
    'hash' => '',
    'user' => '',
    'priority' => '',
    'project' => '',
    'content' => ''
);

$parsingProjectName = false;
foreach(explode(' ', $tweet) as $piece)
{
    switch(substr($piece, 0, 1))
    {
        case '#':
            $data['hash'] = substr($piece, 1);
            break;
        case '@':
            $data['user'] = substr($piece, 1);
            break;
        case '!':
            $data['priority'] = substr($piece, 1);
            break;
        case '[':
            // Check if the project name is longer than 1 word
            if(strpos($piece, -1) == ']')
            {
                $data['project'] = substr($piece, 1, -1);
            }
            else
            {
                // There will be more to parse in the next piece(s)
                $parsingProjectName = true;
                $data['project'] = substr($piece, 1) . ' ';
            }
            break;
        default:
            if($parsingProjectName)
            {
                // Are we at the end yet?
                if(strpos($piece, -1) == ']')
                {
                    // Yes we are
                    $data['project'] .= substr($piece, 1, -1);
                    $parsingProjectName = false;
                }
                else
                {
                    // Nope, there is more
                    $data['project'] .= substr($piece, 1) . ' ';
                }
            }
            else
            {
                // We aren't in the middle of parsing the project name, and this piece doesn't start with one of the special chars, so assume it is content
                $data['content'] .= $piece . ' ';
            }
    }
}

// There will be an extra space on the end; remove it
$data['content'] = substr($data['content'], 0, -1);

【讨论】:

  • 顺便说一句,您可能需要仔细检查我对 substr 的使用情况。我用它来检查第一个字符,最后一个字符,并抓取除第一个或最后一个字符之外的所有内容。我希望它有意义并且有效:P
【解决方案3】:

使用preg_split 而不是preg_match_all,然后您将获得介于两者之间的所有组件,作为 Brent 的答案(返回单个字符串)的替代方案。请注意,许多匹配项可能为空。

【讨论】:

  • 我还输入了一个答案以使用 preg_match 遍历匹配项,保存偏移量以检索数据而不复制任何字符串,但这对于这个用例来说太重了。我猜想去罗马有多种方式。
【解决方案4】:

我认为您的正则表达式中有一个错误,因为您在 [a-zA-Z_] 之前使用 \w 看起来您想匹配空格,而 \w 匹配单词字符。你可以这样做(对于这个小部分):

...\\s*[\\w_]+\\s*...

由于您似乎已经遍历匹配项以获取不同的部分,因此您可以为要匹配的纯文本创建一个子模式,并将其与模式的其余部分或连接起来。这样一来,您只需再进行一场比赛。只要您在遍历匹配项时区分匹配的部分,这将适用于不同的内容顺序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-24
    • 1970-01-01
    • 2021-12-24
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多