Как разобрать текстовую таблицу в C++

Я пытаюсь разобрать таблицу в виде текстового файла, используя ifstream, и оценивая/манипулируя каждой записью. Однако у меня возникли проблемы с выяснением того, как подойти к этому из-за упущения определенных элементов. Рассмотрим следующую таблицу:

NEW  VER  ID   NAME
1    2a   4    "ITEM ONE" (2001)
     1    7    "2 ITEM" (2002) {OCT}
     1.1  10   "SOME ITEM 3" (2003)
1         12   "DIFFERENT ITEM 4" (2004)
1    a4   16   "ITEM5" (2005) {DEC}

Как видите, иногда в столбце «НОВОЕ» ничего нет. Что я хочу сделать, так это принять к сведению идентификатор, имя, год (в скобках) и отметить, есть ли фигурные скобки или нет после этого.

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

Единственное, о чем я могу думать, это читать каждую строку слово за словом, отслеживая последнее число, которое я видел. Как только я нажму на кавычку, обратите внимание, что последнее число, которое я видел, было идентификатором (если я использовал что-то вроде разделения, позицию массива прямо перед кавычкой), а затем записывайте все до следующей цитаты (название) , затем, наконец, начните искать скобки и фигурные скобки для другой информации. Однако это кажется действительно примитивным, и я ищу лучший способ сделать это.

Я делаю это, чтобы отточить свои навыки C++ и работать с большими существующими наборами данных, поэтому я хотел бы использовать C++, если это возможно, но если другой язык (я смотрю на Perl или Python) делает это тривиально простым, я мог бы просто научитесь взаимодействовать с другим языком с C++. Что я сейчас пытаюсь сделать, так это просто просеивать данные, которые в конечном итоге станут объектами в C++, поэтому у меня все еще есть шанс улучшить свои навыки C++.

РЕДАКТИРОВАТЬ: я также понимаю, что это можно выполнить, используя только регулярное выражение, но я хотел бы попробовать использовать другие методы манипулирования файлами/строками, если это возможно.


person julian    schedule 08.11.2010    source источник
comment
Всегда ли первые три столбца имеют фиксированную ширину?   -  person casablanca    schedule 08.11.2010
comment
Как насчет некоторых примеров token_grid из следующих: codeproject.com/KB/recipes/Tokenizer .aspx Они очень эффективны, элегантны и просты в использовании.   -  person    schedule 09.11.2010


Ответы (2)


Если смещения столбцов действительно фиксированы (без табуляции, только настоящие пробелы а-ля 0x20), я бы читал их построчно (string::getline) и разбивал их, используя фиксированные смещения, на набор четыре строки (string::substr).

Затем постобработайте каждый 4-кортеж строк по мере необходимости.

Я бы не стал жестко кодировать смещения, сохраняя их в отдельном входном файле, который описывает формат ввода - например, описание таблицы в SQL Server или другой БД.

person Steve Townsend    schedule 08.11.2010
comment
+1, это именно то, что я бы предложил, если столбцы действительно имеют фиксированную ширину. - person casablanca; 08.11.2010
comment
Если на то пошло, вы можете довольно легко определить ширину столбца на основе строки заголовка. - person Ben Voigt; 08.11.2010
comment
Я бы поместил все это в struct Item, у которого есть operator>>(). - person wilhelmtell; 08.11.2010
comment
Только что проверил файл, и он действительно с интервалом, а не с вкладками. Надо было раньше подумать! Большое спасибо. - person julian; 08.11.2010
comment
Потоки ввода-вывода C++ по умолчанию используют любые пробелы. Таким образом, табуляция или пробелы любой произвольной длины (если есть хотя бы один пробельный символ) среди полей не имеют значения. - person wilhelmtell; 08.11.2010
comment
@wilhelmtell - это было бы проблематично, если бы какой-либо столбец после первого мог быть пустым. - person Steve Townsend; 09.11.2010
comment
@ Стив Конечно. И ваше решение заменяет эту проблему другой (проблемой длин полей). Решение всегда состоит в том, чтобы определить грамматику, которая решает проблему, и придерживаться ее. - person wilhelmtell; 09.11.2010

Что-то вроде этого:

  1. Прочитайте первую строку, найдите "ID" и сохраните индекс.
  2. Прочитайте каждую строку данных, используя std::getline().
  3. Создайте подстроку из строки данных, начиная с индекса, который вы нашли "ID" в строке заголовка. Используйте это для инициализации std::istringstream с помощью.
  4. Прочитайте идентификатор, используя iss >> an_int.
  5. Поиск первого ". Найдите второй ". Найдите ( и запомните его индекс. Найдите ) и запомните этот индекс. Создайте подстроку из символов между этими индексами и используйте ее для инициализации другого std::istringstream. Прочитайте число из этого потока.
  6. Ищите брекеты.
person sbi    schedule 08.11.2010