Чтение строк из файла

Тема в разделе "WASM.BEGINNERS", создана пользователем nolik, 26 окт 2005.

  1. nolik

    nolik New Member

    Публикаций:
    0
    Регистрация:
    25 окт 2005
    Сообщения:
    2
    Имеется файл вида:

    str1

    str2

    str3

    ...

    strN

    Как мне прочитать этот файл в массив, чтоб можно было обращатся к каждой строке, как к элементу массива?
     
  2. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    1.Выделяешь кусок памяти, равный размеру файла и читаешь файл в память.

    2.Проходишь по этому куску памяти и считаешь количество строк в нем.

    3.По количеству строк выделяешь память под массив из расчёта 4 байта на одну строку (вернее на 1 указатель на строку)

    4.Проходишь по куску памяти, в который считан файл, и адреса, соответствующие началам новых строк, записываешь в массив.



    Обращаться к строкам будешь как к участкам памяти, которые адресуются элементами массива указателей. А конкретная реализация зависит от того, какие разделители строк использованы в файле, фиксированная длина строк или переменная и пр. условий.
     
  3. nolik

    nolik New Member

    Публикаций:
    0
    Регистрация:
    25 окт 2005
    Сообщения:
    2
    хм. мне тут идея в голову пришла. Строки заканчиваются обычным переводом строки. Можно читать файл побайтно, пока не встретится символ перевода строки, писать прочитаные байты в массив. щас попробую..
     
  4. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Имхо, это будет очень медленно: читать побайтно.

    Гораздо быстрее считать целиком, а затем в памяти искать перевод строк.

    И кроме того, как ты себе представляешь элемент массива? Как последовательность символов?
     
  5. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Можно сделать примерно так:



    []



    Тут надо бы также добавить проверку возвращаемых API-функциями значений на правильность.
     
  6. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    cresta

    А я бы еще и lpTable разместил в одном блоке памяти lpMemory = VirtuallAlloc вслед за строками (ес-но с выравниваем адреса) и в одном цикле сканирования 0D0A разделял строки нулями и записывал адрес в lpTable ;)
     
  7. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Для этого надо знать количество строк в файле, а на момент сканирования это неизвестно, соответственно неизвестно, сколько памяти выделить под таблицу указателей.

    Можно конечно выделить по-максимуму: 3*file_size (экстремальный случай, когда в файле только переносы строк), но как-то это неправильно. ИМХО.
     
  8. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    > "но как-то это неправильно"

    А резервировать и выделять VirtualAlloc'ом соответственно 64 и 4Кб памяти если file_size окажется < 1-2Кб - это "правильно" ? ;))

    Как известно VirtualAlloc даже при MEM_COMMIT не выделяет физической памяти, реальное выделение страниц происходит only at the first attempt to read or write to that page. Поэтому можно преспокойно выделить и 3*file_size. Но если "жалко", то можно зарезервировать адреса под 3*file_size, а выделить (commit) под таблицу одну страницу, добавив контроль commited_size - для больших файлов это будет намного быстрее и "правильнее", чем дважды сканировать файл ;))

    ИМХО - прелесть VirtualAlloc как раз в том и заключается, что знать точно размер заранее не обязательно (а примерный максимум конечно желательно ;)
     
  9. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Хм, добавление проверки commited_size сожрёт большую часть того, что пытаешься выгадать.

    Человек не знает, как в массив затолкать файл, ему попроще да понаглядней надо. А когда разберётся, тогда и навороты с commit и reserv можно наворачивать.
     
  10. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    cresta

    > "Человек не знает.."

    Поэтому я и адресовал свое примечание тебе ;)



    > "добавление проверки commited_size сожрёт большую часть того, что пытаешься выгадать"

    Если уж на то пошло, то львиную долю времени тут отъедает ReadFile, на фоне которого любые оптимизации могут дать лишь жалкие гроши. Но, как ты верно заметил, часто рулит не критерий разумной достаточности, а "правильности" и "как-то неправильности" ;) А "некрасивость" двух аллоков и двух проходов ИМХО вполне очевидна. Для маленьких файлов вся таблица может вполне уместиться в хвосте файла на уже выделенной 4Кб странице, а в твоем варианте мы делаем отдельный аллок и тратим немалое время на выделение и инициализацию новой страницы (ведь винда при выделении физической памяти под страницу еще и забивает ее нулями). Для больших файлов, превышающих размер L2 в твоем варианте добавятся нехилые тормоза на двойную перекачку данных ОЗУ <-> кэш. Ну а если данные умещаются в кэше, то все равно два прохода - это вдвое дольше чем один ;)

    Теперь посмотрим, что у нас "сожрет" дополнительная проверка commited_size. В реальной ситуации в файле хранятся строки переменной длины (иначе незачем искать разделители 0D0A ;) и значит неизбежны промахи предсказания перехода при поиске разделителей. При случайном чередовании длин будем иметь ~50% промахов с соответсвующими штрафами. Проверка байта (ворда) в цикле занимает не менее 2 тиков, умножаем на среднюю длину строки и добавляем 50%-й штраф = 0.5*(10,20,30) тиков в зависимости от проца. Проверку commited_size мы делам перед (или лучше после) записи указателя в таблицу, т.е. один раз для каждой строки (а не символа). Ясно, что один-два тика cmp (или 0 с учетом возможного распараллеливания) это мелочь по сравнению со средним временем поиска разделителя, да еще с учетом двух проходов. Увеличение MEM_COMMIT будет происходить максимум один раз на 1024 указателя (если выделять память по 1й странице, хотя можно и больше), причем сам вызов VirtualAlloc занимает небольшой процент времени по сравнению с физическим выделением и инициализацией страницы, а это как я уже говорил, неизбежно при любых вариантах использования VirtualAlloc.

    Ну и потом все-таки, желательно иметь какие-то априорные данные. Если человек собирается обращаться с текстовым файлом как с массивом строк, значит ему что-то известно об этих строках (количество, длина - хотя бы порядок величин). Поэтому можно исходя из размера файла выбирать начальное значение и приращение MEM_COMMIT исходя из компромисса между "экономией" памяти и числом приращений (экономия ес-но в кавычках, т.к. как я уже говорил никакой реальной памяти VirtualAlloc не выделяет).

    Все это конечно ИМХО, т.к. у каждого свои представления о "правильности" и "как-то неправильности" ;)
     
  11. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Ну это всё умозрительные заключения, трудно сказать, как на деле будет.