Получить строку/столбец запроса XPath в Pugixml

Мы хотим получить строку/столбец результата запроса xpath в pugixml :

pugi::xpath_query query_child(query_str);
std::string value = Convert::toString(query_child.evaluate_string(root_node));

Мы можем получить смещение, но не строку/столбец:

unsigned int = query_child.result().offset;

Если мы повторно проанализируем файл, мы можем преобразовать смещение => (строка, столбец), но это неэффективно.

Существует ли эффективный метод для достижения этой цели?


person Ghassen Hamrouni    schedule 27.01.2011    source источник


Ответы (1)


  1. result().offset — последнее проанализированное смещение в строке запроса; он будет равен 0, если запрос был успешно проанализирован; так что это не смещение в файле XML.

  2. Для запросов XPath, которые возвращают строки, понятие «смещение в файле XML» не определено, т.е. что вы ожидаете для запроса concat("a", "b")?

  3. Для запросов XPath, которые возвращают узлы, вы можете получить смещение данных узла в файле. К сожалению, из-за производительности синтаксического анализа и потребления памяти эту информацию невозможно получить без повторного синтаксического анализа. В списке TODO есть задача сделать это проще (т.е. парой строчек кода), но это займет некоторое время.

Итак, если вы хотите найти смещение узла, являющееся результатом запроса XPath, единственный способ — получить результат запроса XPath в виде набора узлов (query.evaluate_node_set или node.select_single_node/select_nodes), получить смещение (node.offset_debug()) и преобразовать его в строку/ столбец вручную.

Вы можете подготовить структуру данных для преобразования смещение -> строка/столбец один раз, а затем использовать ее несколько раз; например, следующий код должен работать:

#include <vector>
#include <algorithm>
#include <cassert>
#include <cstdio>

typedef std::vector<ptrdiff_t> offset_data_t;

bool build_offset_data(offset_data_t& result, const char* file)
{
    FILE* f = fopen(file, "rb");
    if (!f) return false;

    ptrdiff_t offset = 0;

    char buffer[1024];
    size_t size;

    while ((size = fread(buffer, 1, sizeof(buffer), f)) > 0)
    {
        for (size_t i = 0; i < size; ++i)
            if (buffer[i] == '\n')
                result.push_back(offset + i);

        offset += size;
    }

    fclose(f);

    return true;
}

std::pair<int, int> get_location(const offset_data_t& data, ptrdiff_t offset)
{
    offset_data_t::const_iterator it = std::lower_bound(data.begin(), data.end(), offset);
    size_t index = it - data.begin();

    return std::make_pair(1 + index, index == 0 ? offset : offset - data[index - 1]);
}

Это не обрабатывает разрывы строк в стиле Mac и не обрабатывает вкладки; это может быть тривиально добавлено, конечно.

person zeuxcg    schedule 27.01.2011
comment
Спасибо, да, я хочу найти смещение node. Но вопрос в том, как преобразовать в строку/столбец без повторного разбора? Может быть, я должен отредактировать код pugixml? - person Ghassen Hamrouni; 27.01.2011
comment
И ответ - нельзя. Вы можете редактировать код pugixml, но это будет не очень просто — по соображениям производительности здесь нет лексера, поэтому нет единого места, где можно было бы считать новые строки. Ваш лучший выбор - повторный анализ; вы можете выполнить повторный анализ за один проход один раз, создав std::map‹int, int› с ключом = смещение новой строки и значением = индекс строки (возрастающее число); затем вы можете использовать equal_range для преобразования смещения в строку + столбец. Нет необходимости изменять код pugixml. - person zeuxcg; 27.01.2011
comment
Но как получить смещение атрибута? - person Ghassen Hamrouni; 24.02.2011