【问题标题】:C++: How to extract words from string with regexC ++:如何使用正则表达式从字符串中提取单词
【发布时间】:2017-12-17 15:22:37
【问题描述】:

我想从字符串中提取单词。我能想到有两种方法可以做到这一点:

  1. 按分隔符提取。
  2. 通过词模式搜索进行提取。

在详细说明问题之前,我想澄清一下,虽然我确实询问了提取方法及其实现,但我的问题的主要焦点是正则表达式;不是实现。

我想匹配的单词可以包含撇号(例如“Don't”),可以在双引号或单引号内(撇号)(例如“Hello”和“world”)以及两者的组合(例如“没有”和“不会”)。它们还可以包含数字(例如“2017”和“U2”)以及下划线和连字符(例如“hello_world”和“time-turner”)。单词中的撇号、下划线和连字符必须被其他单词字符包围。最后一个要求是包含随机非单词字符的字符串(例如“Good mor¨+%g.”)仍应将所有单词字符识别为单词。

从中提取单词的示例字符串以及我希望结果的样子:

  1. "Hello, world!" 应该导致 "Hello""world"
  2. "Aren't you clever?" 应该导致 "Aren't""you""clever"
  3. "'Later', she said." 应该导致 "Later""she""said"
  4. "'Maybe 5 o'clock?'" 应该导致 "Maybe""5""o'clock"
  5. "In the year 2017 ..." 应该导致 "In""the""year""2017"
  6. "G2g, cya l8r" 应该导致 "G2g""cya""l8r"
  7. "hello_world.h" 应该导致 "hello_world""h"
  8. "Hermione's time-turner." 应该导致 "Hermione's""time-turner"
  9. "Good mor~+%g." 应该导致 "Good""mor""g"
  10. "Hi' Testing_ Bye-" 应该导致 "Hi""Testing""Bye"

因为——据我所知——我提出的两种方法需要完全不同的解决方案,我将把我的问题分成两部分——每种方法各一个。

1。按分隔符提取

这是我花费大部分时间来开发的方法,并且我找到了一个部分可行的解决方案 - 但是,我怀疑我使用的正则表达式不是很有效。我的解决方案是这样的(使用 Boost.Regex,因为它的 Perl 语法支持向后查找):

#include <string>
#include <vector>
#include <iostream>
#include <boost/regex.hpp>



std::vector<std::string> phrases({  "Hello, world!", "Aren't you clever?",
                                    "'Later', she said.", "'Maybe 5 o'clock?'",
                                    "In the year 2017 ...", "G2g, cya l8r",
                                    "hello_world.h", "Hermione's time-turner.",
                                    "Good mor~+%g.", "Hi' Testing_ Bye-"});
std::vector<std::string> words;

boost::regex delimiterPattern("^'|[\\W]*(?<=\\W)'+\\W*|(?!\\w+(?<!')'(?!')\\w+)[^\\w']+|'$");
boost::sregex_token_iterator end;
for (std::string phrase : phrases) {
    boost::sregex_token_iterator phraseIter(phrase.begin(), phrase.end(), delimiterPattern, -1);

    for ( ; phraseIter != end; phraseIter++) {
        words.push_back(*phraseIter);
        std::cout << words[words.size()-1] << std::endl;
    }
}

我对这个解决方案最大的问题是我的正则表达式,我认为它看起来太复杂了,可能会做得更好。它也不能正确匹配单词末尾的撇号 - 就像示例 3 中一样。下面是 regex101.com 的链接,其中包含正则表达式和示例字符串:Delimiter regex

2。词模式搜索提取

我自己并没有花太多时间来追求这条道路,主要将其作为替代方案,因为我的部分解决方案不一定是最好的解决方案。我对如何实现这一点的建议是在重复搜索字符串以查找模式的过程中做一些事情,从字符串中删除每个匹配项,直到没有更多匹配项为止。我有一个适用于这种方法的正则表达式,但仍想输入它:"[A-Za-z0-9]+(['_-]?[A-Za-z0-9]+)?"。这是 regex101.com 的链接,其中包含正则表达式和示例字符串:Word pattern regex

我想再次强调,我首先需要输入我的正则表达式,但也感谢实现这些方法的帮助。


编辑:感谢@Galik 指出所有格复数可以以撇号结尾。与这些相关的撇号可以在分隔符中匹配,而不必在单词模式中匹配(即"The kids' toys" 应该导致"The""kids""toys")。

【问题讨论】:

  • 您能否提供您希望成功的当前实施失败的示例?
  • 看来你可以用\w+(?:['-]\w+)*。或者带有单词边界:\b\w+(?:['-]\w+)*\b.
  • @Galik 我当前的实现——假设你的意思是我的方法 1 的正则表达式——在我的第三个示例字符串的情况下和你建议以撇号结尾的所有格复数的情况下都失败了;它与单词末尾的撇号不匹配。
  • @Wiktor 我不使用 \w 的原因是它包含下划线,因此无法正确匹配示例 10 中的 "Testing"
  • @Jaxon 问题不大,减去_。使用\b[^\W_]+(?:['_-][^\W_]+)*\b。我将_ 添加到匹配inword 标点符号的字符类中。

标签: c++ regex string


【解决方案1】:

你可以使用

[^\W_]+(?:['_-][^\W_]+)*

请参阅regex demo

模式详情

  • [^\W_]+ - 一个或多个非单词字符和_ 以外的字符(匹配字母数字字符)
  • (?: - 仅对子模式和匹配进行分组的非捕获组的开始:
    • ['_-] - '_-
    • [^\W_]+ - 1+ 个字母数字字符
  • )* - 重复该组零次或多次。

C++ demo:

std::regex r(R"([^\W_]+(?:['_-][^\W_]+)*)");
std::string s = "Hello, world! Aren't you clever? 'Later', she said. Maybe 5 o'clock?' In the year 2017 ... G2g, cya l8r hello_world.h Hermione's time-turner. Good mor~+%g. Hi' Testing_ Bye- The kids' toys";
for(std::sregex_iterator i = std::sregex_iterator(s.begin(), s.end(), r);
                         i != std::sregex_iterator();
                         ++i)
{
    std::smatch m = *i;
    std::cout << m.str() << '\n';
}

【讨论】:

  • 它就像一个魅力,看起来比我拥有的要好得多 - 谢谢!
猜你喜欢
  • 2011-03-09
  • 1970-01-01
  • 2019-03-18
  • 1970-01-01
  • 2021-10-27
  • 2016-02-24
  • 2017-08-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多