Простой, Пользовательский Разбор с C ++ - PullRequest
4 голосов
/ 23 апреля 2010

Я уже некоторое время читаю SO, но я действительно не могу найти никакой помощи по моей проблеме.

У меня есть назначение на c ++ для создания симулятора IAS.

Вот пример кода ...

0   1   a
1   2   b
2   c
3   1
10  begin
11  . load a, subtract b and offset by -1 for jump+
11  load M(0)
12  sub M(1)
13  sub M(3)
14  halt

Используя c ++, я должен иметь возможность читать эти строки и сохранять их в классе «регистр памяти», который я уже построил ...

Например, первая строка должна хранить «1 a» в нулевом регистре.

Как я могу разобрать число в начале строки, а затем сохранить остаток как строку?

У меня настроено хранилище с использованием класса, который вызывается с помощью mem.set(int, string);. int - это ячейка памяти в начале строки, а string - это сохраненная инструкция.

Редактировать: Некоторые пояснения:

  • Я должен использовать стандартные библиотеки
  • грамматика для входного файла здесь: http://www.cs.uwyo.edu/~seker/courses/2150/iascode.pdf
  • Загрузчик перезапишет дубликаты строк. Это означает, что первая строка 11 в образце будет заменена второй.

Ответы [ 6 ]

2 голосов
/ 23 апреля 2010

Я бы предложил взглянуть на библиотеку <ifstream>.

1 голос
/ 24 апреля 2010

Если первая часть строки всегда является числом, посмотрите на функцию strtoul. Со страницы man:

strtoul - преобразовать строку в длинное целое число без знака

БИБЛИОТЕКА

Стандартная библиотека C (libc, -lc)

СИНТАКСИС

 #include <stdlib.h>
 unsigned long strtoul(const char *restrict str, char **restrict endptr, int base);

ОПИСАНИЕ * * 1016 Функция strtoul () преобразует строку в str в unsigned long значение. Конверсия осуществляется в соответствии с заданной базой, который должен быть между 2 и 36 включительно, или быть специальным значением 0. Строка может начинаться с произвольного количества пробелов (как определено isspace(3)), за которым следует один необязательный знак + или -. Если base - ноль или 16, строка может включать префикс 0x, а номер будет читаться в базе 16; в противном случае нулевое основание принимается за 10 (десятичный), если следующий символ не является 0, в этом случае он принимается как 8 (восьмеричное). Остальная часть строки преобразуется в длинное значение без знака в очевидным образом, остановка в конце строки или в первом символе, который не дает действительной цифры в заданной базе. (В базах выше 10 буква A в верхнем или нижнем регистре обозначает 10, B представляет 11, и так далее, с Z, представляющим 35.) Если endptr не NULL, strtoul() сохраняет адрес первого недействительного символ в *endptr. Однако, если цифр не было вообще, strtoul() сохраняет исходное значение str в *endptr. (Таким образом, если *str не является \0 но **endptr равно \0 при возврате, вся строка была действительной.) ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ

Функция strtoul () возвращает либо результат конвертации, либо, если был ведущий минус знак, отрицание результата преобразования, если оригинал (неотрицательное) значение будет переполнено; в последнем случае strtoul() возвращает ULONG_MAX. Во всех случаях errno установите на ERANGE. Если преобразование не может быть выполнено, возвращается 0 и глобальная переменная errno установлена ​​в EINVAL.


Ключ здесь - параметр endptr. Он устанавливает указатель на то место, где вам нужно продолжить анализ. Если endptr == str, то вы знаете, что строка не начиналась с цифры.

Мне нравится семейство функций strto___ намного больше, чем функции ato__, потому что вы можете установить base (включая контекстную "базу 0") и потому, что endptr говорит мне, где продолжить с. (А для встроенных приложений strto___ занимает гораздо меньше места, чем __scanf функций.)

РЕДАКТИРОВАТЬ: Извините, что пропустил ваш комментарий. Чтобы использовать endptr, напишите код как:

char* restOfLine = NULL;
unsigned long result = strtoul(lineBuffer, 10, &restOfLine);
if(restOfLine == NULL || restOfLine == lineBuffer)
{
     /* Handle error. */
}
else
{
    // Use result, and do further parsing starting at restOfLine.
}

Как правило, предложение «error error» возвращает или разбивает или генерирует исключение, или делает что-то еще, чтобы вырваться из дальнейшей обработки, поэтому вам не понадобится явное предложение else.

1 голос
/ 24 апреля 2010
#include <iostream> // #include <fstream> for file objects as others suggest
#include <string>
#include <map>
using namespace std;

map<int, string> my_program;

int line_num;
string line_text;
while ( cin >> line_num ) { // or any input stream such as a file
    getline( cin, line_text ); // standard function defined in <string>
    my_program[ line_num ] = line_text; // store line for next phase
}

Это будет читать строки файла, пока не встретится конец, или строка, которая начинается с чего-то, кроме числа. Используйте cin.eof(), чтобы проверить, что весь файл был прочитан, если вам интересно.

Конечно, так как map сортирует свое содержимое, строки будут в числовом порядке для следующей фазы.

0 голосов
/ 24 апреля 2010

Разделение ведущего номера и остальной части строки не является слишком сложной задачей. Используйте что-то вроде getline для чтения по одной строке за раз из вашего входного файла и сохраните строку в строку char cur_line[]. Для каждой строки попробуйте что-то вроде этого:

  • Объявить указатель char* pString и целое число int line_num
  • Используйте функцию strstr, чтобы найти первый символ пробела, и присвойте результат pString.
  • Перемещение pString вперед на один символ за раз, пока он не укажет на символ без пробелов. Это начало строки, содержащей «остаток строки».
  • Используйте atoi для cur_line, чтобы преобразовать первую запись в строке в целое число и сохранить результаты в line_num
  • Теперь вы должны иметь возможность вызывать свою функцию, как mem.set(line_num, pString)

Однако интерпретировать эти строки будет гораздо сложнее ...

Edit: Как упоминает Майк DeSimone, вы можете комбинировать шаги strstr и atoi, описанные выше, если вы используете одну из strto* функций вместо atoi .

0 голосов
/ 24 апреля 2010

Вот простая часть:

std::string text_to_parse;
unsigned int register_number;

void Parse_Line(std::istream& data_file)
{
  // Read in the register number.
  if(data_file >> register_number)
  {
    // Read the remaining line as a string {variable}
    getline(data_file, text_to_parse);

    //  Now do something with the text...
  }
  return;
}

Проблема с вашим файлом данных заключается в том, что он не следует простой грамматике или синтаксису. Например, у вас есть две текстовые строки, которые начинаются с 11. Строка 10 - это не строка «регистр памяти», а строка инструкций. Кроме того, строки 2 и 3 не следуют той же грамматике, что и 0 и 1.

Для получения дополнительной помощи, пожалуйста, опубликуйте правила грамматики (предпочтительно в синтаксисе BNF или в искусстве ASCII).

0 голосов
/ 24 апреля 2010

Примерно так может быть полезно использование библиотеки Boost.Spirit. Это генератор синтаксического анализа EBNF в C ++, такой как flex и yacc без дополнительных шагов компиляции.

...