Чтение формата файла с использованием C / C ++ надлежащим образом - PullRequest
0 голосов
/ 15 апреля 2011

Как бы вы прочитали простой формат файла, используя C или C ++?

Например, взять формат файла Wavefront .obj, пример:

# this is a box

o 1

v -0.5 -0.5 0.5
v -0.5 -0.5 -0.5
v -0.5 0.5 -0.5
v -0.5 0.5 0.5
v 0.5 -0.5 0.5
v 0.5 -0.5 -0.5
v 0.5 0.5 -0.5
v 0.5 0.5 0.5

usemtl Default
f 4 3 2 1
f 2 6 5 1
f 3 7 6 2
f 8 7 3 4
f 5 8 4 1
f 6 7 8 5

Поскольку файлы могут быть довольно большими, я создал промежуточный класс (FileBuffer) с оператором []. Он содержит 4096 байт файла в памяти и считывает новые части при необходимости. Формат файла действительно прост, но я не предпочитаю использовать что-то вроде flex / bison для этого. Это только усложнит дело.

Как правильно интерпретировать этот (вид) файл? В настоящее время у меня есть множество вложенных циклов for / while и отслеживание множества счетчиков. Также много операторов switch / elseif. Как бы я сделал этот код более понятным и понятным?

Спасибо.

Ответы [ 3 ]

2 голосов
/ 15 апреля 2011

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

struct Command { /* Abstract Base Class */ ... };
struct VCommand : Command { std::vector<double> dims; ... }
struct FCommand : Command { std::vector<int> vertexes; ... }
struct FootCommand : Command { enum {LEFT, RIGHT, IN, OUT} e; ... }
std::vector<Command*> commandList; // DANGER: raw pointers
void ParseInput(std::istream& in) {
    // untested code:
    std::string line;
    while(getline(in, line)) {
        std::stringstream lineStream(line);
        std::string command;
        lineStream >> command;
        if(command == "v") {
            std::istream_iterator<double>(lineStream) begin;
            std::istream_iterator<double> end;

            // Add the "v" command to the parse tree
            commandList.push_back(new VCommand(begin, end));
        } else if (command == "f") {
            std::istream_iterator<int>(lineStream) begin;
            std::istream_iterator<int> end;

            // Add the "v" command to the parse tree
            commandList.push_back(new FCommand(begin, end));
        } else if (command == "quit") {
            ...
        } else if (command == "put_your_left_foot_in") {
            ...
            commandList.push_back(new FootCommand(LEFT, IN));
        }
    }
}
1 голос
/ 15 апреля 2011

Если я правильно понимаю формат, каждая строка определяет определенный тип данных, с типом, определенным первым слово. Я бы начал с определения абстрактного базового класса и конкретный экземпляр класса для каждого типа линии; Я бы зарегистрируйте эти экземпляры в std::map<std::string, LineParser*>.

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

std::string line;
while ( std::getline( filteredInput, line ) ) {
    std::string keyword;
    std::istringstream toParse( line );
    toParse >> keyword;
    std::map<std::string, LineParser*>::const_iterator
        parser = registry.find( keyword );
    if ( parser == registry.end() ) {
        //  Syntax error: unknown keyword...
    } else {
        parser->parse( toParse );
    }
}
0 голосов
/ 15 апреля 2011

Я бы начал с определения (или получения) грамматики / структуры файла.

Затем создайте парсер для входного потока на основе этой грамматики.

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