2015-03-05 6 views
0

Я пытаюсь подражать работе простого ассемблера (как часть моего курса CS) по следующей входной строке : «MOV R \ nADD R \ nSUB 30 \ nSTORE 1000 \ nHALT"C++ регулярное выражение, соответствующее нескольким строкам

Я хочу отделить отдельные инструкции от этой строки. Поэтому я использовал следующий код regex: «^. + $», что означает, что он должен совпадать с 1 или более символами, начинающимися и заканчивающимися новыми строками.

Однако функция C++ regex_match не получает совпадений для этого шаблона. Но онлайн-тестер показал мне, что шаблон дает именно то, что мне нужно.

Вот мой фрагмент кода пытается извлечь инструкции:

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

Я использую Visual Studio 2013. Я пробовал использовать следующие шаблоны, а также:.

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

Пожалуйста, не используйте регулярные выражения для разбора кода сборки. Сборочный код не является примером обычного языка и поэтому не может быть правильно проанализирован регулярным выражением. – Dai

+0

Хотя вы используете регулярное выражение как средство разделения строки (и если вы используете C/C++, просто используйте 'strtok'), это плохая идея с точки зрения потребления памяти. Вместо этого используйте простой парсер с конечным автоматом (и он также будет поддерживать потребление памяти). – Dai

+0

Но онлайн-тестер смог правильно разобрать его. – MrWarlock616

ответ

0

Вы используете якоря ^ и $ в однострочном режиме, поэтому ^ соответствует началу строки, а $ соответствует концу строки, а не привязывается к строкам.

C++ regex библиотека не имеет возможности многострочный/однострочные, что .NET имеют Регулярные выражения, так что вы хотите использовать regex_search вместо regex_match, но, как я сказал в моем замечании-ответ на Ваше оригинальное сообщение : вы не должны использовать регулярные выражения для анализа ассемблерного кода, а при использовании регулярного выражения в качестве грубого инструмента для токенирования используется свайный драйвер, когда вам нужен только молоток: strtok - ваш друг.

char* input = "MOV R\nADD R\nSUB 30\nSTORE 1000\nHALT"; 
const char* delimiters = " \n" 

char* token = strtok(input, delimiters ); 
while(token != nullptr) { 

    cout << token << endl; 
    token = strtok(nullptr, delimiters); 
} 

Обратите внимание, что strtok является состоянием, что объясняет недетерминированный характер последующих вызовов к strtok с nullptr в качестве первого аргумента. Это описано здесь: http://www.cplusplus.com/reference/cstring/strtok/

отметить также, что strtok фактически изменяет входной строки, следовательно, почему input является char*, а не const char* const или string (потому что string::c_str() возвращает const char*).

strtok является, к сожалению, одной из функций C, которая, к сожалению, не имеет C++ - идиоматической альтернативы. Вы можете использовать метод Boost string::split, но это вводит новое распределение памяти, тогда как strtok изменяет буфер строки на месте, чтобы преобразовать разделители в \0, чтобы вы могли использовать значения char* с нулевыми функциями строки - поэтому у него есть свои преимущества (то есть без дополнительных необходимо выделить память или дополнительное строковое копирование).


Более полное решение:

Проблема strtok в том, что вы не знаете, какой разделитель был просто перезаписывается, поэтому если у вас есть грамматика, придающую различные разделители различных семантических (как в вашем случае : '' (пробел) является операндом-разделителем, а `` \ n 'является разделителем кода операции.

Решение состоит в том, чтобы иметь копию строки, которая не была изменена, и использовать относительные указатели для получить индекс символа, чтобы определить, какой разделитель был встречен. Это несколько поражает точку usi нг strtok, чтобы свести к минимуму использования памяти, хотя, но это не избавляет от необходимости проверять каждое возвращаемое значение как средство обеспечения соблюдения ваших правил грамматики:

const char input[] = "MOV R\nADD R\nSUB 30\nSTORE 1000\nHALT"; 
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); 
} 
+0

Хорошо, но посмотрите документацию по ECMAScript по адресу http://www.cplusplus.com/reference/regex/ECMAScript/ В нем говорится, что^подразумевает: «Либо это начало целевой последовательности или следует за терминатором линии ». и $ подразумевает: «Либо это начало целевой последовательности, либо следует за терминатором линии». Линейные терминаторы включают «\ n», поэтому это должно сработать. Во всяком случае, я использовал регулярное выражение, потому что думал, что будет проще (и этот документ ввел меня в заблуждение), но ваше решение чистое и простое, так что спасибо :) – MrWarlock616

+0

@ MrWarlock616 '^' и '$' соответствуют новостям только тогда, когда вы используя опции 'match_flag_type'. Посмотрите на 'match_not_bol' и' match_not_eol' (для '^' и '$' соответственно). – Dai

+0

К сожалению, я имел в виду, что $ означает «Либо это конец целевой последовательности, либо предшествует терминатору строк». – MrWarlock616

Смежные вопросы