Загрузка/сохранение объекта map в файл.

Тема в разделе "LANGS.C", создана пользователем KeSqueer, 30 сен 2010.

  1. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    Существует текстовый файл следующего формата:
    Код (Text):
    1. ASCII_представление_хэша_1 = строка_1
    2. ASCII_представление_хэша_2 = строка_2
    3. ...
    4. ASCII_представление_хэша_N = строка_N
    причём (основная загвоздка здесь), строка_i может быть пустой, а может быть очень длинной. Фактически, формат совпадает с форматом *.ini файлов (за исключением наличия в оных секций).

    Требуется загрузить этот файл в контейнер map(вообще это не столь важно, куда загружать).
    Если использовать istream>>, то не понятно, как обходиться со знаком "=" и пустой строкой после "=". Точнее, понятно - после stream >> _string обрезать ненужные символы в начале строки.
    Переход от C++ к C, и использование fscanf, fgets предполагает наличие буфера определённого размера, а, как сказано выше, строка после знака "=" может быть очень длинной, т.е. это не лучший вариант.
    Можно читать побайтно с поиском '\n' и парсить полученную строку, но это неэффективно(?).

    Кто может предложить наиболее изящный метод решения этой задачи?
     
  2. gazlan

    gazlan Member

    Публикаций:
    0
    Регистрация:
    22 май 2005
    Сообщения:
    414
    Не понял, что именно смущает. Без буфера все равно не обойтись (можно выделить память в куче).

    Вариантов ровно два: 1. читать в большой (макс. размера) буфер (gets) и отбросить лишнее и 2. на первом проходе выяснить размер (strchr), на втором проходе прочитать нужную часть и вручную сдвинуть указатель за конец строки (удобно при мэппинге файла в память).

    IMHO, первое проще и быстрее + гарантирует правильную трансляцию символов конца строки (встречаются дурные файлы, где OD, OA идут поодиночке и парами/тройками в непредсказуемых комбинациях).

    К слову, fgets и собственный парсинг всегда предпочтительнее, чем fscanf и кропотливая ловля ошибок ввода/форматирования в мегабайтных файлах.

    Потоки в C++, на мой взгляд, удобны для сериализации, но не для форматного I/O.
     
  3. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    gets -- это вообще не вариант. Забудьте про эту функцию. Любое использование этой функции -- это гарантированный баг в программе типа "переполнение буфера". Если очень хочется, используйте fgets.
    Насчёт форматного i/o в C++ я не осведомлён. Но могу предложить такой вариант: прочитать всю строку в динамически выделяемый буфер*, а потом вручную разобрать.

    *) в STL где-то есть функция getline. Либо можно написать вручную на malloc/realloc. Но если вручную, то тогда уж проще сразу разбирать строку и выделять память под хеш и значение, а не под всю строку.

    Какая разница, после чего ловить ошибки форматирования в мегабайтных файлах? Найдёшь ли ты эту ошибку scanf'ом или вручную -- всё равно её исправлять придётся.
     
  4. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    r90
    getline решила :) спасибо за наводку.
    Вдогонку вопрос: есть ли в STL функция которая удалит все wite-space в начале строки? Можно использовать string::find_first_not_of(" \t") + string::erase(), но, может есть уже нечто подобное?
     
  5. W4FhLF

    W4FhLF New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2006
    Сообщения:
    1.050
    KeSqueer

    Если есть возможность использовать boost, то для первой задачи подошёл бы boost::tokenizer (или при более сложных конструкциях парсер на основе boost::qi || boost::karma, пишется легко, компактный, очень гибкий и быстрый ). Для работы со строками (в частности удаление пробелов и подобное) хорошо подходит boost::algorithm и в частности boost::trim || boost::replace || boost::erase.

    Если же ты сам этот файл составляешь,, записываешь и читаешь, то проще юзать boost::serialization, в котором существуют сериалайзеры для всех стандартных контейнеров.
     
  6. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    W4FhLF
    Давно смотрел в сторону boost, но мне эта библиотека казалась слишком сложной, в C++ я всё же новичек ещё. Видимо придётся. По задаче, наверное, boost::serialization подойдёт как раз.
     
  7. W4FhLF

    W4FhLF New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2006
    Сообщения:
    1.050
    Ну вряд ли буст будет слишком сложен, если ты юзаешь STL. Тем более сейчас в буста неплохая справка и в гугле полно примеров.

    Сериализация std::map в бинарном формате и обратно будет выглядеть следующим образом:

    Код (Text):
    1. #include <iostream>
    2. #include <fstream>
    3. #include <iterator>
    4. #include <algorithm>
    5. #include <functional>
    6. #include <numeric>
    7. #include <map>
    8.  
    9. #include <boost/bind.hpp>
    10. #include <boost/assign/std/map.hpp>
    11. #include <boost/archive/xml_iarchive.hpp>
    12. #include <boost/archive/xml_oarchive.hpp>
    13. #include <boost/serialization/map.hpp>
    14.  
    15. using namespace boost::assign;
    16.  
    17. int main(int argc, char* argv[])
    18. {
    19.     typedef std::map<std::string, std::string> StringMap;
    20.    
    21.     // Save
    22.     {
    23.         StringMap smap;
    24.  
    25.         insert(smap)("Hash1", "String1")("Hash2", "String2")
    26.             ("Hash3", "String3")("Hash4", "String4");
    27.  
    28.         std::ofstream ofs("serializedMap.bin", std::ios::binary);
    29.         boost::archive::binary_oarchive oa(ofs);
    30.  
    31.         oa & smap;
    32.     }
    33.  
    34.     // Load
    35.     {
    36.         StringMap smap;
    37.  
    38.         std::ifstream ifs("serializedMap.bin", std::ios::binary);
    39.         boost::archive::binary_iarchive ia(ifs);
    40.  
    41.         ia & smap;
    42.  
    43.         // Let's see the result
    44.         std::ostream_iterator<string> oit(cout, "\n");
    45.         std::transform(smap.begin(), smap.end(),
    46.             oit, bind(&StringMap::value_type::second, _1));
    47.     }
    48. }
     
  8. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    W4FhLF
    Прокомментируйте, пожалуйста, строки:
    Код (Text):
    1. insert(smap)("Hash1", "String1")("Hash2", "String2")
    2.             ("Hash3", "String3")("Hash4", "String4");
    и
    Код (Text):
    1. std::transform(smap.begin(), smap.end(),
    2.             oit, bind(&StringMap::value_type::second, _1));
     
  9. W4FhLF

    W4FhLF New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2006
    Сообщения:
    1.050
    Первое - это feature из boost::assign. Эквивалентно:

    Код (Text):
    1. smap.insert(std::make_pair("Hash1", "String1"));
    2. smap.insert(std::make_pair("Hash2", "String2"));
    Запись с использованием boost::assign короче и нагляднее.

    Второе - это простой лямба-функтор. Берём поочерёдно все value из std::map (т.е. std::pair::second) и присваиваем созданому потоковому итератору oit, который выводит всё в поток cout через разделители "\n".

    Это вместо цикла. :)
    На самом деле это всё навороты, можно обойтись и без этого.
     
  10. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    KeSqueer

    Все уже придумано до нас.

    Код (Text):
    1. boost::property_tree::ptree tree;
    2. boost::property_tree::ini_parser::read_ini("config.ini", tree);
     
  11. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Код (Text):
    1. std::string str = "  ololo bugaga     ";
    2. boost::algorithm::trim(str);