Как преобразовать строку в шаблонный тип в C ++ - PullRequest
4 голосов
/ 31 августа 2011

Я хочу прочитать и проанализировать текстовый файл в C ++ в общем виде. Файл всегда состоит из пар ключ-значение, по одной на строку. Ключ настраивается так же, как и значение. Я предвижу, что ключ и значения всегда будут базовым типом (int, float, string).

Моя проблема в том, что я не знаю, как создать преобразование строки ключа или значения в правильный тип.

Я попробовал следующее:

template<class Key, class T> inline
void EventReportReader<Key, T>::validateFileFormat()
{
    // Read file line by line and check that the first token is of type Key and the second one of type T

    std::string line;
    try {
        boost::regex re( "(\\S+)\\s+(.*)" );
        while( getline( inStream_, line ) ) {
            boost::cmatch matches;
            if( boost::regex_match( line.c_str(), matches, re ) ) {
                std::cout << re << " matches " << line << std::endl;
                std::cout << "   1st : " << matches[1] << "\n   2nd : " << matches[2] << std::endl;
                // test types
                Key *k = dynamic_cast<Key*>(&matches[1]);
                T t = dynamic_cast<T>(matches[2]);
            }
        }
    }
    catch( boost::regex_error& e ) {
        // todo problem with regular expression, abort
    }
}

И использование этого метода заключается в следующем:

// This in turn calls the method validateFileFormat
EventReportReader<float, int> reader( testFileName );

Результат

/ home / vonhalle / dev / EventBasedReport / libs / event_based_report / EventReportReader.h: 121: 60: ошибка: невозможно dynamic_cast '(const boost :: sub_match *) совпадений. = const char *, Allocator = std :: allocator>, boost :: match_results :: const_reference = const boost :: sub_match & '(типа' const struct boost :: sub_match ') для типа' float ' (цель не указатель или ссылка на класс) /home/vonhalle/dev/EventBasedReport/libs/event_based_report/EventReportReader.h:122:53: ошибка: невозможно dynamic_cast 'совпадения >, boost :: match_results :: const_reference = const boost :: sub_match & '(типа' const struct boost :: sub_match ') для типа' int '(цель не указатель или ссылка)

Как мне это сделать? Это вообще возможно?

EDIT: Файл может выглядеть следующим образом, если шаблон

1.14 5
2.34 78
0.56 24

или это, если шаблон

23 asdf
45 2222
1 bbbb

EDIT2:

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

Ответы [ 3 ]

4 голосов
/ 31 августа 2011

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

Более простой подход - просто использовать потоки C ++.
Эти потоковые объекты уже знают, как читать все основные типы. И любой, кто хочет что-либо делать в C ++, добавит соответствующие операторы ввода и вывода для потоковой передачи своего класса; поэтому довольно универсально, что вы сможете читать любой тип как ключ, так и значение (с ограничением, что он должен помещаться в одну строку).

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

Попробуйте это:

#include <string>
#include <memory>
#include <fstream>
#include <sstream>
#include <vector>
#include <iterator>
#include <algorithm>

// These can be any types.    
typedef std::string   Key;
typedef int           Value;

// The data type to hold the data.
template<typename K,typename V>
class Data: public std::pair<K, V>
{

};

Вот код, который будет читать одну запись из одной строки файла:
Обратите внимание, что тип данных Data и этот оператор ввода являются шаблонными и, таким образом, могут готовить пары ключ / значение любых объектов (если эти объекты знают, как выполнять потоковую передачу).

template<typename K,typename V>
std::istream& operator>>(std::istream& stream, Data<K,V>& data)
{
    // Read a line into a local string.
    std::string  line;
    std::getline(stream,line);

    // convert the line into a stream and read the key/value from the line
    std::stringstream  linestream(line);
    linestream >> data.first >> data.second;


    // If the linestream is bad, then reading the key/value failed
    // If reading one more `char` from the linestream works then there is extra crap in the line
    // thus we have bad data on a line.
    //
    // In either case set the bad bit for the input stream.
    char c;
    if ((!linestream) || (linestream >> c))
    {
        stream.setstate(std::ios::badbit);
    }
    // return the stream.
    return stream;
}

Теперь использовать его просто означает использовать поток:

int main()
{
    // The input file
    std::ifstream      file("Plop");

    // We will convert the file and store it into this vector.
    std::vector<Data<Key,Value> >  data;

    // Now just copy the data from the stream into the vector.
    std::copy(std::istream_iterator<Data<Key,Value> >(file),
            std::istream_iterator<Data<Key, Value> >(),
            std::back_inserter(data)
            );
}

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

2 голосов
/ 31 августа 2011

dynamic_cast должен использоваться в иерархиях наследования; ты неправильно здесь используешь.

Для такой простой задачи вы могли бы использовать потоки, я думаю (предупреждение, не проверено):

template<class Key, class T>
void EventReportReader<Key, T>::validateFileFormat()
{
    std::string line;
    while( getline( inStream_, line ) ) {
        std::istringstream stream(line);
        Key k; T t;
        stream >> k >> t;

        if (!stream || !stream.eof()) { /*error*/ }
    }
}

Есть ли причина, по которой вы не сохраняете прочитанные значения?

РЕДАКТИРОВАТЬ : Давайте специализироваться на строках

template<class Key>
void EventReportReader<Key, std::string>::validateFileFormat()
{
    std::string line;
    while( getline( inStream_, line ) ) {
        size_t const ws = line.find(' ');
        if (ws == std::string::npos) { /* error */ }

        std::istringstream stream(line.substr(0, ws));
        Key k;
        stream >> k;

        if (!stream) { /*error*/ }

        std::string t = line.substr(ws);
        boost::trim(t);
    }
}
1 голос
/ 31 августа 2011

Без информации вы не можете быть уверены, что вы получили правильный тип или нет. Ваше значение может быть строкой или int. Например, вы можете разобрать его с помощью этого алгоритма:

  1. Разберите его как int, если он не успешен
  2. Разобрать как float, если нет
  3. Это строка

Опять же, вы могли успешно проанализировать int, но вы ожидаете строку.


Относительно вашей реализации: вам, вероятно, нужна частичная специализация шаблона.

template<class Key> inline
void EventReportReader<Key, int>::validateFileFormat()
{
  // readLine
  // parse as int..
}

template<class Key> inline
void EventReportReader<Key, float>::validateFileFormat()
{
  // readLine
  // parse as float..
}

Но, вероятно, лучше не шаблонировать вашу функцию? Если у вас есть «нормальная» функция, вы можете использовать логику разбора, которую я описал ранее ..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...