我使用的语言只有()
,|
,空格和字母字符。
给出如下的正则表达式:
(hello|goodbye) (world(s|)|)
如何生成以下数据?
hello worlds
hello world
hello
goodbye worlds
goodbye world
goodbye
我不太确定我是否需要先构建一个树,或者是否可以递归完成。我坚持使用什么数据结构,以及如何生成字符串。我是否必须保留一堆标记,并将其索引回部分构建的字符串以连接更多数据?我不知道如何最好地解决这个问题。我是否需要首先阅读整个表达式,并以某种方式重新排序?
函数签名将采用以下方式:
std::vector<std::string> Generate(std::string const&){
//...
}
你建议我做什么?
修改
让我澄清一下,结果在这里应该总是有限的。在我的特定示例中,表达式中只有6个字符串。我不确定我的术语在这里是否正确,但我正在寻找的是表达式的完美匹配 - 而不是任何包含匹配的子字符串的字符串。
答案 0 :(得分:2)
在Kieveli的建议之后,我提出了一个有效的解决方案。虽然之前没有提到过,但对我来说,重要的是还要计算可能产生多少结果。我正在使用一个名为“exrex”的python脚本,我在github上找到了它。令人尴尬的是,我没有意识到它有能力计算。尽管如此,我使用简化的正则表达式语言在C ++中尽我所能地实现了它。如果对我的解决方案感兴趣,请继续阅读。
从面向对象的角度来看,我编写了一个扫描程序来获取正则表达式(字符串),并将其转换为标记列表(字符串向量)。然后将令牌列表发送到生成n-ary树的解析器。所有这些都包含在一个“表达式生成器”类中,该类可以获取表达式并保存解析树以及生成的计数。
扫描仪很重要,因为它标记了空字符串案例,您可以在我的问题中看到它显示为“|)”。扫描还创建了[字] [操作] [字] [操作] ... [字]的模式。
例如,扫描:"(hello|goodbye) (world(s|)|)"
将创建:[][(][hello][|][goodbye][)][ ][(][world][(][s][|][][)][][|][][)][]
解析树是节点的向量。节点包含节点向量的向量。
橙色单元格表示“或”,而绘制连接的其他框表示“和”。以下是我的代码。
节点标题
#pragma once
#include <string>
#include <vector>
class Function_Expression_Node{
public:
Function_Expression_Node(std::string const& value_in = "", bool const& more_in = false);
std::string value;
bool more;
std::vector<std::vector<Function_Expression_Node>> children;
};
节点来源
#include "function_expression_node.hpp"
Function_Expression_Node::Function_Expression_Node(std::string const& value_in, bool const& more_in)
: value(value_in)
, more(more_in)
{}
扫描标题
#pragma once
#include <vector>
#include <string>
class Function_Expression_Scanner{
public: Function_Expression_Scanner() = delete;
public: static std::vector<std::string> Scan(std::string const& expression);
};
扫描仪来源
#include "function_expression_scanner.hpp"
std::vector<std::string> Function_Expression_Scanner::Scan(std::string const& expression){
std::vector<std::string> tokens;
std::string temp;
for (auto const& it: expression){
if (it == '('){
tokens.push_back(temp);
tokens.push_back("(");
temp.clear();
}
else if (it == '|'){
tokens.push_back(temp);
tokens.push_back("|");
temp.clear();
}
else if (it == ')'){
tokens.push_back(temp);
tokens.push_back(")");
temp.clear();
}
else if (isalpha(it) || it == ' '){
temp+=it;
}
}
tokens.push_back(temp);
return tokens;
}
Parser标题
#pragma once
#include <string>
#include <vector>
#include "function_expression_node.hpp"
class Function_Expression_Parser{
Function_Expression_Parser() = delete;
//get parse tree
public: static std::vector<std::vector<Function_Expression_Node>> Parse(std::vector<std::string> const& tokens, unsigned int & amount);
private: static std::vector<std::vector<Function_Expression_Node>> Build_Parse_Tree(std::vector<std::string>::const_iterator & it, std::vector<std::string>::const_iterator const& end, unsigned int & amount);
private: static Function_Expression_Node Recursive_Build(std::vector<std::string>::const_iterator & it, int & total); //<- recursive
//utility
private: static bool Is_Word(std::string const& it);
};
解析源
#include "function_expression_parser.hpp"
bool Function_Expression_Parser::Is_Word(std::string const& it){
return (it != "(" && it != "|" && it != ")");
}
Function_Expression_Node Function_Expression_Parser::Recursive_Build(std::vector<std::string>::const_iterator & it, int & total){
Function_Expression_Node sub_root("",true); //<- contains the full root
std::vector<Function_Expression_Node> root;
const auto begin = it;
//calculate the amount
std::vector<std::vector<int>> multiplies;
std::vector<int> adds;
int sub_amount = 1;
while(*it != ")"){
//when we see a "WORD", add it.
if(Is_Word(*it)){
root.push_back(Function_Expression_Node(*it));
}
//when we see a "(", build the subtree,
else if (*it == "("){
++it;
root.push_back(Recursive_Build(it,sub_amount));
//adds.push_back(sub_amount);
//sub_amount = 1;
}
//else we see an "OR" and we do the split
else{
sub_root.children.push_back(root);
root.clear();
//store the sub amount
adds.push_back(sub_amount);
sub_amount = 1;
}
++it;
}
//add the last bit, if there is any
if (!root.empty()){
sub_root.children.push_back(root);
//store the sub amount
adds.push_back(sub_amount);
}
if (!adds.empty()){
multiplies.push_back(adds);
}
//calculate sub total
int or_count = 0;
for (auto const& it: multiplies){
for (auto const& it2: it){
or_count+=it2;
}
if (or_count > 0){
total*=or_count;
}
or_count = 0;
}
/*
std::cout << "---SUB FUNCTION---\n";
for (auto it: multiplies){for (auto it2: it){std::cout << "{" << it2 << "} ";}std::cout << "\n";}std::cout << "--\n";
std::cout << total << std::endl << '\n';
*/
return sub_root;
}
std::vector<std::vector<Function_Expression_Node>> Function_Expression_Parser::Build_Parse_Tree(std::vector<std::string>::const_iterator & it, std::vector<std::string>::const_iterator const& end, unsigned int & amount){
std::vector<std::vector<Function_Expression_Node>> full_root;
std::vector<Function_Expression_Node> root;
const auto begin = it;
//calculate the amount
std::vector<int> adds;
int sub_amount = 1;
int total = 0;
while (it != end){
//when we see a "WORD", add it.
if(Is_Word(*it)){
root.push_back(Function_Expression_Node(*it));
}
//when we see a "(", build the subtree,
else if (*it == "("){
++it;
root.push_back(Recursive_Build(it,sub_amount));
}
//else we see an "OR" and we do the split
else{
full_root.push_back(root);
root.clear();
//store the sub amount
adds.push_back(sub_amount);
sub_amount = 1;
}
++it;
}
//add the last bit, if there is any
if (!root.empty()){
full_root.push_back(root);
//store the sub amount
adds.push_back(sub_amount);
sub_amount = 1;
}
//calculate sub total
for (auto const& it: adds){ total+=it; }
/*
std::cout << "---ROOT FUNCTION---\n";
for (auto it: adds){std::cout << "[" << it << "] ";}std::cout << '\n';
std::cout << total << std::endl << '\n';
*/
amount = total;
return full_root;
}
std::vector<std::vector<Function_Expression_Node>> Function_Expression_Parser::Parse(std::vector<std::string> const& tokens, unsigned int & amount){
auto it = tokens.cbegin();
auto end = tokens.cend();
auto parse_tree = Build_Parse_Tree(it,end,amount);
return parse_tree;
}
生成器标题
#pragma once
#include "function_expression_node.hpp"
class Function_Expression_Generator{
//constructors
public: Function_Expression_Generator(std::string const& expression);
public: Function_Expression_Generator();
//transformer
void Set_New_Expression(std::string const& expression);
//observers
public: unsigned int Get_Count();
//public: unsigned int Get_One_Word_Name_Count();
public: std::vector<std::string> Get_Generations();
private: std::vector<std::string> Generate(std::vector<std::vector<Function_Expression_Node>> const& parse_tree);
private: std::vector<std::string> Sub_Generate(std::vector<Function_Expression_Node> const& nodes);
private:
std::vector<std::vector<Function_Expression_Node>> m_parse_tree;
unsigned int amount;
};
生成源
#include "function_expression_generator.hpp"
#include "function_expression_scanner.hpp"
#include "function_expression_parser.hpp"
//constructors
Function_Expression_Generator::Function_Expression_Generator(std::string const& expression){
auto tokens = Function_Expression_Scanner::Scan(expression);
m_parse_tree = Function_Expression_Parser::Parse(tokens,amount);
}
Function_Expression_Generator::Function_Expression_Generator(){}
//transformer
void Function_Expression_Generator::Set_New_Expression(std::string const& expression){
auto tokens = Function_Expression_Scanner::Scan(expression);
m_parse_tree = Function_Expression_Parser::Parse(tokens,amount);
}
//observers
unsigned int Function_Expression_Generator::Get_Count(){
return amount;
}
std::vector<std::string> Function_Expression_Generator::Get_Generations(){
return Generate(m_parse_tree);
}
std::vector<std::string> Function_Expression_Generator::Generate(std::vector<std::vector<Function_Expression_Node>> const& parse_tree){
std::vector<std::string> results;
std::vector<std::string> more;
for (auto it: parse_tree){
more = Sub_Generate(it);
results.insert(results.end(), more.begin(), more.end());
}
return results;
}
std::vector<std::string> Function_Expression_Generator::Sub_Generate(std::vector<Function_Expression_Node> const& nodes){
std::vector<std::string> results;
std::vector<std::string> more;
std::vector<std::string> new_results;
results.push_back("");
for (auto it: nodes){
if (!it.more){
for (auto & result: results){
result+=it.value;
}
}
else{
more = Generate(it.children);
for (auto m: more){
for (auto r: results){
new_results.push_back(r+m);
}
}
more.clear();
results = new_results;
new_results.clear();
}
}
return results;
}
总之,如果您需要为正则表达式生成匹配项,我建议使用exrex或此主题中提到的任何其他程序。
答案 1 :(得分:1)
当我使用自己的自定义小语言时,我先写了一个解析器。解析器在内存中创建了一个表示文本的结构。对于这种小语言,我会创建一个类似这样的结构:
Node:
list of string values
isRequired
list of child Nodes
解析文本时,您将获得一个节点列表:
Node1:
hello, goodbye
true
[] (no child nodes)
Node2:
world,
false
[
Node3:
s,
false
[]
]
一旦你解析了这个结构,你可以想象一下代码,它会产生你想要的东西,你知道什么必须包含,以及可能包括什么。伪代码看起来像这样
recursiveGenerate( node_list, parital )
if ( node_list is null or is empty )
add partial to an output list
for the first node
if ( ! node.isRequired )
recursiveGenrate( remaining nodes, partial )
for each value
recursiveGenerate( child Nodes + remaining nodes, partial + value )
这应该按照你想要的方式填充你的列表。
答案 2 :(得分:1)
我写这篇文章是为了支持文本到语音发音词典中的正则表达式,但正则表达式扩展逻辑是自包含的。您可以像使用它一样使用它:
import dictionary
words, endings = dictionary.expand_expression('colou?r', {})
print words
这里,第二个参数用于引用(即命名块),而结尾用于例如, look{s,ed,ing}
工作原理......
lex_expression
将字符串拆分为由正则表达式标记[]<>|(){}?
分隔的标记。因此,a(b|cd)efg
变为['a', '(', 'b', '|', 'cd', ')', 'efg']
。这样可以更容易地解析正则表达式。
parse_XYZ_expr
函数(以及顶级parse_expr
)解析正则表达式元素,构造表示正则表达式的对象层次结构。这些对象是:
a?
)因此,ab(cd|e)?
表示为Sequence(Literal('ab'), Optional(Choice(Literal('cd'), Literal('e'))))
。
这些类支持expand
形式为expr.expand(words) => expanded
的方法,例如:
expr = Optional('cd')
print expr.expand(['ab', 'ef'])
结果:
ab
abcd
ef
efcd
答案 3 :(得分:1)
让我重新发布一个较旧的答案:
我曾写过little program这样做:
其工作原理如下:
全部? {} + * | ()运算符被扩展(达到最大限制),因此只保留字符类和反向引用。
e.g。 [a-c]+|t*|([x-z]){2}foo\1|(a|b)(t|u)
变为[a-c]|[a-c][a-c]|[a-c][a-c][a-c]|[a-c][a-c][a-c][a-c]||t|tt|tt|ttt|ttt|([x-z][x-z])foo\1|at|au|bt|bu
(后面的表达式只是表示法,程序将每个备用子规则保留在列表中)
多个字符的反向引用将替换为单个字符的反向引用。
e.g。上面的表达式变为[a-c]|[a-c][a-c]|[a-c][a-c][a-c]|[a-c][a-c][a-c][a-c]||t|tt|tt|ttt|ttt|([x-z])([x-z])foo\1\2|at|au|bt|bu
现在每个替代subregex都匹配一个固定长度的字符串。
对于每个替代方案,都会打印所有类别中的挑选字符组合:
e.g。上面的表达式变为a|b|c|aa|ba|..|cc|aaa|baa|...|ccc|aaaa|...|cccc||t|tt|tt|ttt|ttt|xxfooxx|yxfooyx|...|zzfoozz|at|au|bt|bu
如果您只想要计数(通常足够快,因为步骤2的输出通常远远短于最终输出),您可以跳过第3步。