使用正则表达式匹配和提取数据
Posted
技术标签:
【中文标题】使用正则表达式匹配和提取数据【英文标题】:Matching and Extracting data using regular expression 【发布时间】:2015-11-14 17:36:06 【问题描述】:问题:找到匹配的字符串并从匹配的字符串中提取数据。有许多带有关键字和数据的命令字符串。
命令示例:
-
问名字给我打电话
通知执行此操作的名称
请求的消息名称
关键字:询问、通知、消息、到、那个。数据:
输入字符串:
-
让彼得给我打电话
通知珍娜我要离开了
给家里发消息说我迟到了
我的问题包括两个问题 1) 查找匹配命令 2) 提取数据
这是我正在做的事情: 我创建了多个正则表达式: "问[[:s:]][[:w:]]+[[:s:]]到[[:s:]][[:w:]]+" 或 "问([^\t\ n]+?)到([^\t\n]+?)" "通知[[:s:]][[:w:]]+[[:s:]]那个[[:s:]][[:w:]]+" 或 "通知([^\t\ n]+?)那个([^\t\n]+?)"
void searchExpression(const char *regString)
std::string str;
boost::regex callRegEx(regString, boost::regex_constants::icase);
boost::cmatch im;
while(true)
std::cout << "Enter String: ";
getline(std::cin, str);
fprintf(stderr, "str %s regstring %s\n", str.c_str(), regString);
if(boost::regex_search(str.c_str(), im, callRegEx))
int num_var = im.size() + 1;
fprintf(stderr, "Matched num_var %d\n", num_var);
for(int j = 0; j <= num_var; j++)
fprintf(stderr, "%d) Found %s\n",j, std::string(im[j]).c_str());
else
fprintf(stderr, "Not Matched\n");
我能够找到匹配的字符串,但无法提取数据。 这是输出:
input_string: Ask peter to call Regex Ask[[:s:]][[:w:]]+[[:s:]]to[[:s:]][[:w:]]+
Matched num_var 2
0) Found Ask peter to call
1) Found
2) Found
我想提取 peter 并从 Ask Peter 中调用。
【问题讨论】:
我不确定 Boost.Regex,但通常你会得到整个匹配的字符串作为结果中的索引 0 以及你在其他索引中定义的任何组。您没有定义任何组,所以这似乎是您的问题。也就是说,您使用+1
和 >=
的索引似乎很可疑,请仔细阅读文档。
【参考方案1】:
既然你真的想要解析一个语法,你应该考虑使用 Boost 的解析器生成器。
您只需自上而下地编写整个内容:
auto sentence = [](auto&& v, auto&& p)
auto verb = lexeme [ no_case [ as_parser(v) ] ];
auto name = lexeme [ +graph ];
auto particle = lexeme [ no_case [ as_parser(p) ] ];
return confix(verb, particle) [ name ];
;
auto ask = sentence("ask", "to") >> lexeme[+char_];
auto notify = sentence("notify", "that") >> lexeme[+char_];
auto message = sentence("message", "that") >> lexeme[+char_];
auto command = ask | notify | message;
这是一个 Spirit X3 语法。将lexeme
读作“保留整个单词”(不要忽略空格)。
在这里,“名称”被认为是任何与预期粒子相关的内容¹
如果你只想返回匹配的原始字符串,这就足够了:
Live On Coliru
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/directive/confix.hpp>
namespace x3 = boost::spirit::x3;
namespace commands
namespace grammar
using namespace x3;
auto sentence = [](auto&& v, auto&& p)
auto verb = lexeme [ no_case [ as_parser(v) ] ];
auto name = lexeme [ +graph ];
auto particle = lexeme [ no_case [ as_parser(p) ] ];
return confix(verb, particle) [ name ];
;
auto ask = sentence("ask", "to") >> lexeme[+char_];
auto notify = sentence("notify", "that") >> lexeme[+char_];
auto message = sentence("message", "that") >> lexeme[+char_];
auto command = ask | notify | message;
auto parser = raw [ skip(space) [ command ] ];
int main()
for (std::string const input :
"Ask peter to call me",
"Notify Jenna that I am going to be away",
"Message home that I am running late",
)
std::string matched;
if (parse(input.begin(), input.end(), commands::grammar::parser, matched))
std::cout << "Matched: '" << matched << "'\n";
else
std::cout << "No match in '" << input << "'\n";
打印:
Matched: 'Ask peter to call me'
Matched: 'Notify Jenna that I am going to be away'
Matched: 'Message home that I am running late'
奖金
当然,您实际上是想提取相关的信息。
我会这样做。让我们解析成一个结构体:
struct Command
enum class Type ask, message, notify type;
std::string name;
std::string message;
;
让我们把main()
写成:
commands::Command cmd;
if (parse(input.begin(), input.end(), commands::grammar::parser, cmd))
std::cout << "Matched: " << cmd.type << "|" << cmd.name << "|" << cmd.message << "\n";
else
std::cout << "No match in '" << input << "'\n";
Live On Coliru
#include <iostream>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/directive/confix.hpp>
namespace x3 = boost::spirit::x3;
namespace commands
struct Command
enum class Type ask, message, notify type;
std::string name;
std::string message;
friend std::ostream& operator<<(std::ostream& os, Type t) return os << static_cast<int>(t); // TODO
;
BOOST_FUSION_ADAPT_STRUCT(commands::Command, type, name, message)
namespace commands
namespace grammar
using namespace x3;
auto sentence = [](auto type, auto&& v, auto&& p)
auto verb = lexeme [ no_case [ as_parser(v) ] ];
auto name = lexeme [ +graph ];
auto particle = lexeme [ no_case [ as_parser(p) ] ];
return attr(type) >> confix(verb, particle) [ name ];
;
using Type = Command::Type;
auto ask = sentence(Type::ask, "ask", "to") >> lexeme[+char_];
auto notify = sentence(Type::notify, "notify", "that") >> lexeme[+char_];
auto message = sentence(Type::message, "message", "that") >> lexeme[+char_];
auto command // = rule<struct command, Command>
= ask | notify | message;
auto parser = skip(space) [ command ];
int main()
for (std::string const input :
"Ask peter to call me",
"Notify Jenna that I am going to be away",
"Message home that I am running late",
)
commands::Command cmd;
if (parse(input.begin(), input.end(), commands::grammar::parser, cmd))
std::cout << "Matched: " << cmd.type << "|" << cmd.name << "|" << cmd.message << "\n";
else
std::cout << "No match in '" << input << "'\n";
打印
Matched: 0|peter|call me
Matched: 2|Jenna|I am going to be away
Matched: 1|home|I am running late
¹我不是英语语言学家,所以我不知道这是否是正确的语法术语:)
【讨论】:
【参考方案2】:此代码从文件“commands.txt”中读取命令字符串,搜索正则表达式并在匹配时打印部分。
#include <iostream>
#include <fstream>
#include <string>
#include <boost/regex.hpp>
const int NumCmdParts = 4;
std::string CommandPartIds[] = "Verb", "Name", "Preposition", "Content";
int main(int argc, char *argv[])
std::ifstream ifs;
ifs.open ("commands.txt", std::ifstream::in);
if (!ifs.is_open())
std::cout << "Error opening file commands.txt" << std::endl;
exit(1);
std::string cmdStr;
// Pieces of regular expression pattern
// '(?<Verb>' : This is to name the capture group as 'Verb'
std::string VerbPat = "(?<Verb>(Ask)|(Notify|Message))";
std::string SeparatorPat = "\\s*";
std::string NamePat = "(?<Name>\\w+)";
// Conditional expression. if (Ask) (to) else (that)
std::string PrepositionPat = "(?<Preposition>(?(2)(to)|(that)))";
std::string ContentPat = "(?<Content>.*)";
// Put the pieces together to compose pattern
std::string TotalPat = VerbPat + SeparatorPat + NamePat + SeparatorPat
+ PrepositionPat + SeparatorPat + ContentPat;
boost::regex actions_re(TotalPat);
boost::smatch action_match;
while (getline(ifs, cmdStr))
bool IsMatch = boost::regex_search(cmdStr, action_match, actions_re);
if (IsMatch)
for (int i=1; i <= NumCmdParts; i++)
std::cout << CommandPartIds[i-1] << ": " << action_match[CommandPartIds[i-1]] << "\n";
ifs.close();
【讨论】:
我认为唯一使问题相关的是“通知”与“那个”一起使用,而“询问”与“到”一起使用。你的语法忽略了区别。 好收获。我会用条件表达式修复它 我喜欢命名的捕获+1以上是关于使用正则表达式匹配和提取数据的主要内容,如果未能解决你的问题,请参考以下文章