【问题标题】:Tokenize a String and Keep Delimiters Using Regular Expression in C++在 C++ 中使用正则表达式标记字符串并保留分隔符
【发布时间】:2017-12-05 04:35:23
【问题描述】:

我想修改给定的正则表达式以生成以下匹配列表。我很难用语言描述这个问题。

我想使用正则表达式来匹配一组“令牌”。具体来说,我希望匹配&&,||,;,(,),并且任何不包含这些字符的字符串都应该是匹配的。 我遇到的问题是区分一根管子和两根管子。我怎样才能产生所需的匹配?非常感谢您的帮助!

Link to this example

表达式:

((&{2})|(\|{2})|(\()|(\))|(;)|[^&|;()]+)

测试字符串

a < b | c | d > e >> f && ((g) || h) ; i

预期匹配

a < b | c | d > e >> f 
&&

(
(
g
)

||
 h
)

;
 i

实际匹配

a < b 
|
 c 
|
 d > e >> f 
&&

(
(
g
)

||
 h
)

;
 i

我正在尝试为 C++ 中的程序实现自定义标记器。

示例代码

std::vector<std::string> Parser::tokenizeInput(std::string s) {
    std::vector<std::string> returnTokens;

    //tokenize correctly using this regex
    std::regex rgx(R"S(((&{2})|(\|{2})|(\()|(\))|(;)|[^&|;()]+))S");

    std::regex_iterator<std::string::iterator> rit ( s.begin(), s.end(), rgx );
    std::regex_iterator<std::string::iterator> rend;

    while (rit!=rend) {

        std::string tokenStr = rit->str();

        if(tokenStr.size() > 0 && tokenStr != " "){
            //assure the token is not blank
            //and push the token
            boost::algorithm::trim(tokenStr);
            returnTokens.push_back(tokenStr);
        }

        ++rit;
    }

    return returnTokens;
}

示例驱动程序代码

//in main
std::vector<std::string> testVec = Parser::tokenizeInput(inputWithNoComments);
std::cout << "input string: " << inputWithNoComments << std::endl;
std::cout << "tokenized string[";
for(unsigned int i = 0; i < testVec.size(); i++){
    std::cout << testVec[i];
    if ( i + 1 < testVec.size() ) { std::cout << ", "; }
}
std::cout << "]" << std::endl;

产生的输出

input string: (cat file > outFile) || ( ls -l | grep -i )
tokenized string[(, cat file > outFile, ), ||, (, ls -l, grep -i, )]

input string: a && b || c > d >> e < f | g
tokenized string[a, &&, b, ||, c > d >> e < f, g]

input string: foo | bar || foo || bar | foo | bar
tokenized string[foo, bar, ||, foo, ||, bar, foo, bar]

我想要的输出是什么

input string: (cat file > outFile) || ( ls -l | grep -i )
tokenized string[(, cat file > outFile, ), ||, (, ls -l | grep -i, )]

input string: a && b || c > d >> e < f | g
tokenized string[a, &&, b, ||, c > d >> e < f | g]

input string: foo | bar || foo || bar | foo | bar
tokenized string[foo | bar, ||, foo, ||, bar | foo | bar]

【问题讨论】:

  • 您使用哪种编程语言?我们可以尝试编写一个方法来做到这一点。使用 Java String split() 会很容易。
  • 我正在使用 C++,我会更新我的问题以包含它。
  • 你试过我的解决方案了吗?
  • 也许this 可以吗?

标签: c++ regex tokenize


【解决方案1】:

我建议通过将{-1,0} 传递给sregex_token_iterator 来收集不匹配和匹配的子字符串,并使用更简单的正则表达式(如&amp;&amp;|\|\||[;()])同时丢弃空子字符串(这是由于方式找到连续匹配时拆分字符串):

std::regex rx(R"(&&|\|\||[();])");
std::string exp = "a < b | c | d > e >> f && ((g) || h) ; i";
std::sregex_token_iterator srti(exp.begin(), exp.end(), rx, {-1, 0});
std::vector<std::string> tokens;
std::remove_copy_if(srti, std::sregex_token_iterator(), 
                std::back_inserter(tokens),
                [](std::string const &s) { return s.empty(); });
for( auto & p : tokens ) std::cout <<"'"<< p <<"'"<< std::endl;

C++ demo,输出:

'a < b | c | d > e >> f '
'&&'
' '
'('
'('
'g'
')'
' '
'||'
' h'
')'
' '
';'
' i'

空字符串删除代码的特殊功劳归于Jerry Coffin

【讨论】:

  • 这是完美的,它产生了我想要的输出,而且简单干净。我刚刚修改了我的方法以包含此代码。非常感谢!
【解决方案2】:

您尚未指定您使用的语言,但大多数应用程序语言都支持在此正则表达式上拆分字符串:

" *((?=(\$\$|\|\||[;()])|(?<=\$\$|\|\|)|(?<=[;()])) *"

正则表达式是向前看或向后看您的术语,但环顾四周,输入不会被消耗,因此分隔符将输出到结果数组。

如果你使用 python,事情就简单多了;拆分这个正则表达式:

" *(\$\$|\|\||[;()]) *"

任何分隔符被捕获,成为输出数组的一部分。

【讨论】:

  • 感谢您的回答。我正在使用 C++。不过,我似乎无法让您的第一个正则表达式起作用。挑战是我需要在输出数组中保留分隔符。你知道是否有像你在 C++ 中描述的拆分函数吗?
  • @Brett 试试this
【解决方案3】:

我准备了以下正则表达式并对其进行了测试,它产生的输出与您输入字符串中描述的完全相同:

(?<=&&)[^;()]*|\(|\)|(?<=\|\|)[^;()]*|;|&&|\|\||([^|;()&]+(\‌​|[^|;()&]+)*)*

或者这个:

\(|\)|;|&&|\|\||([^|;()&]+(&[^|;()&]+|\|[^|;()&]+)*)

让我知道它是否按预期工作!

匹配:

a < b | c | d > e >> f 
&&

(
(
g
)

||
 h
)

;
 i

并测试:

(cat file > outFile) || ( ls -l | grep -i )
(cat file >> outFile) && ls -l | grep -i
((file < file) || ls -l ; ls)
cat < InputFile | tr a-z A-Z | tee out1 > out2 >> out3 | asd aasdasd  | asd | asd || asd | asd
a | b || c | d && a || b && d ; g && 
a && b || c > d >> e < f | g
a < b | c | d > e >> f && ((g) || h) ; i

【讨论】:

  • 感谢您的回答,它几乎可以工作,但请看看其中一些测试用例:regex101.com/r/fDf5VC/2
  • (?&lt;=&amp;&amp;)[^;()]*|\(|\)|(?&lt;=\|\|)[^;()]*|;|&amp;&amp;|\|\||([^|;()&amp;]+(\|[^|;()&amp;]+)*)* 应该适合你!我已经测试过了,它看起来很棒
  • 或者那个\(|\)|;|&amp;&amp;|\|\||([^|;()&amp;]+(&amp;[^|;()&amp;]+|\|[^|;()&amp;]+)*)
  • 感谢您为此所做的工作!看看这个例子regex101.com/r/fDf5VC/3 我希望a &lt; b | c | d &gt; e &gt;&gt; f 是一个匹配而不是两个
  • 好的,我知道了,我去看看!
猜你喜欢
  • 2013-09-27
  • 2012-08-13
  • 2014-11-22
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多