К сожалению, вы не дали столько информации о том, что вы хотите проанализировать.Если вы дадите (пожалуйста, отредактируйте свой вопрос), то я напишу для вас пример парсера.
Пока я могу дать вам только общие пояснения по парсерам.
Все зависит от того, какваши данные структурированы.Вы должны понимать формальные языки и грамматики, которые могут выражать ваше представление информации в ASCII.Существует также так называемая иерархия Хомского, которая классифицирует языки и описывает, как реализовать синтаксический анализатор.
Ваше утверждение относительно
Мой наивный первый гость простовыполните сравнение строк if (! strcmp (...)) для ключевых слов, а затем разбейте строку по позициям для получения информации.
сработает, если ваши данные будут так называемыми повторениями Хомского типа-3язык.Вы не будете использовать strcmp () или другие C-функции, но std :: regex, чтобы сопоставлять шаблоны в тексте ASCII, а затем возвращать некоторые результаты, окрашенные атрибутами.
Но ваш пример
.START_CMD info1 info2 info3 * extensionnal_info1 ... .END
указывает, что у вас есть некоторые вложенные данные с составными спецификаторами.Это не может быть выражено обычными языками Хомского типа-3.Регулярные выражения, обычно реализуемые как DFA (детерминированный конечный автомат), не могут учитываться.У них нет памяти.Знают только их текущее состояние.Таким образом, они не могут сопоставить количество некоторых «открывающих» операторов с «закрывающими».Это невозможно.
Для описания такого языка вам нужна грамматика и лучшая контекстно-свободная грамматика (CFG).И парсер будет реализован с использованием автомата.Вы бы использовали "Parse Stack".И этот стек будет содержать всю дополнительную информацию.Это память, которой нет у выражений повторения.
И, на мой взгляд, такого подхода будет достаточно для ваших целей
Теперь, как это реализовать.Есть несколько вариантов:
- Lex / Flex Yacc / Bison: чрезвычайно мощный.Трудно понять и реализовать
- Boost Spirit: аналогично вышеописанному.Также нужно некоторое время, чтобы разобраться
- Парсер ручной работы: Много работы.
Если вы начнете с парсера, созданного вручную, вы узнаете больше всего и поймете, как работает синтаксический анализ.Я продолжу с этим в моих объяснениях.
Стандартное решение - парсер сдвига / уменьшения.
И вам понадобится грамматика с продукцией (и обычно действиями)
Вам понадобится TokenTypes для поиска, чтения и использования лексем входных данных.Обычно это реализуется с помощью соответствия выражений regurlar.
Тогда вам понадобятся токены с атрибутами.Сканер / Lexer или просто функция getToken прочитает введенный текст и «токенизирует» его.Затем он возвращает токены с атрибутами (атрибутом является, например, значение целого числа) в анализатор.
Анализатор помещает токен в стек.Затем он пытается сопоставить вершину стека с правой стороной производства.Если есть совпадение, стек уменьшается на количество элементов в правой части производства и заменяется нулевым терминалом на левой стороне производства.И вызывается действие.
Это повторяется до тех пор, пока не будут сопоставлены все входные данные или не будет обнаружена синтаксическая ошибка.
Сейчас я покажу вам некоторый (НЕ Скомпилированный. НЕ ПРОВЕРЕННЫЙ) псевдокод
#include <vector>
#include <string>
#include <variant>
#include <functional>
#include <iostream>
// Here we store token types for Terminals and None-Terminals
enum class TokenType {END, OK, EXPRESSION, START1, END1, START2, END2, INTEGER, DOUBLE, STRING};
struct TokenWIthAttribute {
TokenWIthAttribute(const TokenType &tt) : tokenType(tt) {}
TokenWIthAttribute(const TokenWIthAttribute &twa) : tokenType(twa.tokenType) {}
TokenType tokenType{};
std::variant<int, double, std::string> attribute{};
bool operator ==(const TokenWIthAttribute& twa) const { return tokenType == twa.tokenType;}
};
using NonTerminal = TokenType;
using Handle = std::vector<TokenWIthAttribute>;
using Action = std::function<TokenWIthAttribute(TokenWIthAttribute&)>;
struct Production {
NonTerminal nonTerminal{}; //Left side of Production
Handle handle{}; //Rigth side of prodcution
Action action; //Action to take during reduction
};
using Grammar = std::vector<Production>;
TokenWIthAttribute actionEndOK(TokenWIthAttribute& twa) {
// Do something with twa
return twa;
}
Grammar grammar{
{ TokenType::OK, {TokenType::START1, TokenType::EXPRESSION, TokenType::END1, TokenType::END},actionEndOK}
// Many lines of more productions
};
using ParseStack = std::vector<TokenWIthAttribute>;
class Parser
{
public:
bool parse(std::istream &is);
protected:
TokenWIthAttribute getToken(std::istream &is);
void shift(TokenWIthAttribute& twa) { parseStack.push_back(twa); }
bool matchAndReduce();
ParseStack parseStack;
};
bool Parser::matchAndReduce()
{
bool result{ false };
// Iterate over all productions in the grammar
for (const Production& production : grammar) {
if (production.handle.size() <= parseStack.size()) {
// If enough elements on the stack, match the top of the stack with a production
if (std::equal(production.handle.begin(), production.handle.end(), parseStack.cend() - production.handle.size())) {
// Found production: Reduce
parseStack.resize(parseStack.size() - production.handle.size());
// Call action. Replace right side of production with left side
parseStack.emplace_back(production.action(*(parseStack.begin()+parseStack.size()-1)));
result = true;
break;
}
}
}
return result;
}
int main()
{
std::cout << "Hello World\n";
return 0;
}
Надеюсь, это даст вам первое впечатление.