Идеи разбора строки C ++ - PullRequest
       10

Идеи разбора строки C ++

6 голосов
/ 14 февраля 2011

У меня есть выходные данные другой программы, которая больше предназначена для чтения человеком, чем для чтения машиной, но все равно собираюсь ее проанализировать. Ничего сложного.

Тем не менее, мне интересно, как лучше всего это сделать на C ++. Это больше вопрос типа «общая практика».

Я заглянул в Boost.Spirit и даже немного поработал. Эта вещь сумасшедшая! Если бы I разрабатывал язык, который я читал, это могло бы быть правильным инструментом для работы. Но так как это, учитывая крайнее время компиляции, несколько страниц ошибок из g ++, когда я делаю что-то не так, это просто не то, что мне нужно. (У меня нет особой необходимости в производительности во время выполнения.)

Думая об использовании оператора C ++ <<, но это кажется бесполезным. Если в моем файле есть такие строки, как «Джон имеет 5 виджетов» и другие «Мэри работает на улице Рэмси, 459», как я могу убедиться, что в моей программе есть строка первого типа, а не второго? Я должен прочитать всю строку, а затем использовать такие вещи, как <code>string::find и string::substr Я думаю.

И это оставляет sscanf. Было бы прекрасно справиться с вышеуказанными случаями

if( sscanf( str, "%s has %d widgets", chararr, & intvar ) == 2 )
      // then I know I matched "foo has bar" type of string, 
      // and I now have the parameters too

Так что мне просто интересно, что я что-то упустил или C ++ действительно не имеет много встроенных альтернатив.

Ответы [ 7 ]

3 голосов
/ 14 февраля 2011

sscanf действительно очень хорошо подходит для ваших требований:

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

Потенциальная проблема заключается в том, что она подвержена ошибкам, иу вас много часто меняющихся фраз, тогда усилия по тестированию и риск могут быть тревожными.Сохраняя дух sscanf, но используя istream для безопасности типов:

#include <iostream>
#include <sstream>

// Str captures a string literal and consumes the same from an istream...
// (for non-literals, better to have `std::string` member to guarantee lifetime)
class Str
{
  public:
    Str(const char* p) : p_(p) { }
    const char* c_str() const { return p_; }
  private:
    const char* p_;
};

bool operator!=(const Str& lhs, const Str& rhs)
{
    return strcmp(lhs.c_str(), rhs.c_str()) != 0;
}

std::istream& operator>>(std::istream& is, const Str& str)
{
    std::string s;
    if (is >> s)
        if (s.c_str() != str)
            is.setstate(std::ios_base::failbit);
    return is;
}

// sample usage...

int main()
{
    std::stringstream is("Mary has 4 cats");
    int num_dogs, num_cats;

    if (is >> Str("Mary") >> Str("has") >> num_dogs >> Str("dogs"))
    {
        std::cout << num_dogs << " dogs\n";
    }
    else if (is.clear(), is.seekg(0), // "reset" the stream...
             (is >> Str("Mary") >> Str("has") >> num_cats >> Str("cats")))
    {
        std::cout << num_cats << " cats\n";
    }
}
2 голосов
/ 14 февраля 2011

Инструменты GNU flex и bison - это очень мощные инструменты, которые вы можете использовать по духу, но (по мнению некоторых людей) их проще использовать, отчасти потому, что отчеты об ошибках немного лучше, поскольку инструментыесть свои компиляторы.Это, или Spirit, или какой-то другой генератор синтаксического анализатора, является «правильным» путем, поскольку он дает вам наибольшую гибкость в вашем подходе.

Если вы думаете об использовании strtok, вывместо этого, возможно, стоит взглянуть на stringstream, который разделяется на пробелы и позволяет вам делать некоторые хорошие преобразования форматирования между строками, примитивами и т. д. Он также может быть включен в алгоритмы STL и позволяет избежать всех беспорядочных деталей в сыром Cв стиле управления строковой памятью.

1 голос
/ 14 февраля 2011

Я использовал Boost.Regex (который я тоже считаю tr1 :: regex).Прост в использовании.

1 голос
/ 14 февраля 2011

Я написал обширный код синтаксического анализа на C ++.Это прекрасно работает, но я сам написал код и не полагался на более общий код, написанный кем-то другим.C ++ не поставляется с уже написанным обширным кодом, но это отличный язык для написания такого кода.

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

Если вы можете сделать вопрос более конкретным, я был бы рад предложить вамконкретный ответ.

0 голосов
/ 14 февраля 2011

В зависимости от того, что именно вы хотите анализировать, вам вполне может потребоваться библиотека регулярных выражений. См. msdn или предыдущий вопрос .

Лично, опять же, в зависимости от точного формата, я бы подумал об использовании perl для первоначального преобразования в более машиночитаемый формат (например, запись переменной CSV), а затем гораздо проще импортировать в C ++.

Если вы придерживаетесь C ++, вам необходимо:

  1. Определите запись - надеюсь, просто линия
  2. Определите тип записи - используйте регулярное выражение
  3. Разбор записи - scanf в порядке

Базовый класс по строкам:

class Handler
{
public:
    Handler(const std::string& regexExpr)
        : regex_(regexExpr)
    {}
    bool match(const std::string& s)
    {
        return std::tr1::regex_match(s,regex_);
    }
    virtual bool process(const std::string& s) = 0;
private:
    std::tr1::basic_regex<char> regex_;
};

Определите производный класс для каждого типа записи, вставьте экземпляр каждого в набор и ищите совпадения.

class WidgetOwner : public Handler
{
public:
    WidgetOwner()
        : Handler(".* has .* widgets")
    {}
    virtual bool process(const std::string& s) 
    {
        char name[32];
        int widgets= 0;
        int fieldsRead = sscanf( s.c_str(),  "%32s has %d widgets", name, & widgets) ;

        if (fieldsRead == 2)
        {
            std::cout << "Found widgets in " << s << std::endl;
        }
        return fieldsRead == 2;
    }
};

struct Pred 
{
    Pred(const std::string& record)
        : record_(record)
    {}
    bool operator()(Handler* handler)
    {
        return handler->match(record_);
    }
    std::string record_;
};

std::set<Handler*> handlers_;
handlers_.insert(new WidgetOwner);
handlers_.insert(new WorkLocation);

Pred pred(line);
std::set<Handler*>::iterator handlerIt = 
     std::find_if(handlers_.begin(), handlers_.end(), pred);
if (handlerIt != handlers_.end())
    (*handlerIt)->process(line);
0 голосов
/ 14 февраля 2011

Посмотрите на strtok .

0 голосов
/ 14 февраля 2011

всегда есть strtok () Полагаю,

...