C++正则表达式匹配多行

C++ regex matching multiple lines

本文关键字:正则表达式 C++      更新时间:2023-10-16

我正试图在以下输入字符串上模拟一个简单汇编程序的工作(作为CS课程的一部分):"MOV R\nADD R\nSUB 30\nSTORE 1000\nHALT"

我想把各个指令从那个字符串中分离出来。因此,我使用了以下正则表达式模式"^.+$",这意味着它应该匹配一个或多个以新行开头和结尾的字符。

但是,C++regex_match函数没有得到该模式的任何匹配。但一位在线测试人员向我展示了这种模式正是我所需要的。

以下是我试图提取指令的代码片段:

regex regInst("^.+$", regex::flag_type::icase | regex::flag_type::ECMAScript);
string input = "MOV RnADD RnSUB 30nSTORE 1000nHALT";
smatch instructions, opcode, operand;
regex_match(input, instructions, regInst); // *instructions* is empty after this

我正在使用Visual Studio 2013。我也尝试过使用以下模式:

  • ^(.+)$
  • (^.+$)+

您在单行模式中使用锚^$,因此^只匹配字符串的开头,而$匹配字符串的结尾,而不是锚定到行尾。

C++regex库没有.NET regex所具有的多行/单行选项,所以您可能希望使用regex_search而不是regex_match,但正如我在回复您的原始帖子的评论中所说:您不应该使用正则表达式来解析程序集代码,而使用regex作为粗标记化工具就是在您只需要一把锤子的情况下使用打桩机:strtok是您的朋友。

char* input = "MOV RnADD RnSUB 30nSTORE 1000nHALT";
const char* delimiters = " n"
char* token = strtok( input, delimiters  );
while( token != nullptr ) {
    cout << token << endl;
    token = strtok( nullptr, delimiters );
}

请注意,strtok是有状态的,这解释了以nullptr为第一个参数的对strtok的后续调用的不确定性。此处记录了这一点:http://www.cplusplus.com/reference/cstring/strtok/

还要注意,strtok实际上修改了输入字符串,因此inputchar*而不是const char* conststring(因为string::c_str()返回const char*)。

不幸的是,strtok是C函数中的一个,它没有C++惯用的替代方法。您可以使用Boost的string::split方法,但这引入了新的内存分配,而strtok修改了字符串缓冲区,将分隔符转换为,这样您就可以使用带有null终止字符串函数的char*值,因此它有其优点(即不需要分配额外的内存或复制额外的字符串)。


更完整的解决方案:

strtok的一个问题是,您不知道哪个分隔符刚刚被覆盖,所以当您的语法赋予不同的分隔符不同的语义时(例如:""(空格)是操作数分隔符,"\n"是操作码分隔符。

解决方案是拥有一个未修改的字符串副本,并使用相对指针来获取字符索引,以确定遇到了什么分隔符。不过,这确实在一定程度上违背了使用CCD_26来最大限度地减少内存使用的观点,但它确实消除了测试每个返回值作为强制执行语法规则的方法的必要性:

const char input[] = "MOV RnADD RnSUB 30nSTORE 1000nHALT";
char* temp = calloc( sizeof(input), sizeof(char) );
strcpy( temp, input );
const char* delimiters = " n"
char* token = strtok( temp, delimiters  );
while( token != nullptr ) {
    char delimiter = input[ token - temp ];
    cout << token << endl;
    switch( delimiter ) {
        case ' ':
            cout << token << " ";
            break;
        case 'n':
            cout << endl << token << " ";
            break;
    }
    token = strtok( nullptr, delimiters );
}