我想从字符串中提取单词。我可以想到两种方法可以完成此任务:
用定界符提取。
通过单词模式搜索提取。
在深入探讨问题之前,我想澄清一下,尽管我确实询问了提取方法及其实现,但问题的主要焦点是正则表达式。不是实现。
我要匹配的单词可以包含撇号(例如“ Do n't”),可以在双引号或单引号(撇号)(例如“ Hello”和“ world”)之内,以及两者的组合(例如“ Didn” t”和“不会”)。它们还可以包含数字(例如“ 2017”和“ U2”)以及下划线和连字符(例如“ hello_world”和“ time-turner”)。单词中的撇号,下划线和连字符必须由其他单词字符包围。最后的要求是,包含随机非单词字符(例如“ Goodmor¨+%g。”)的字符串仍应将所有单词字符识别为单词。
从中提取单词的示例字符串以及我想要的结果看起来像什么:"Hello, world!"
应该导致"Hello"
和"world"
"Aren't you clever?"
应该导致"Aren't"
,"you"
和"clever"
"'Later', she said."
应该导致"Later"
,"she"
和"said"
"'Maybe 5 o'clock?'"
应该导致"Maybe"
,"5"
和"o'clock"
"In the year 2017 ..."
应导致"In"
,"the"
,"year"
和"2017"
"G2g, cya l8r"
应该导致"G2g"
,"cya"
和"l8r"
"hello_world.h"
应该导致"hello_world"
和"h"
"Hermione's time-turner."
应该导致"Hermione's"
和"time-turner"
"Good mor~+%g."
应该导致"Good"
,"mor"
和"g"
"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中所示。这是带有regex和示例字符串的
2.通过单词模式搜索提取
我自己没有花太多时间去追求这条路,主要是将它作为替代方案,因为我的部分解决方案不一定是最好的解决方案。关于如何完成此操作,我的建议是按照重复搜索字符串的方式进行操作,在运行时从字符串中删除每个匹配项,直到不再有匹配项为止。我为此方法使用了一个正则表达式,但仍希望输入:
"[A-Za-z0-9]+(['_-]?[A-Za-z0-9]+)?"
。这是带有regex和示例字符串的regex101.com的链接:Word pattern regex。我想再次强调,我首先要在我的正则表达式上输入内容,但也希望对实现这些方法有所帮助。
编辑:感谢@Galik指出所有格可以以撇号结尾。与它们相关的撇号可以在定界符中匹配,而不必在单词模式中匹配(即
"The kids' toys"
应该导致"The"
,"kids"
和"toys"
)。
最佳答案
您可以使用
[^\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';
}
关于c++ - C++:如何使用正则表达式从字符串中提取单词,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45070539/