Количество разделителей строк

Тема в разделе "WASM.A&O", создана пользователем cresta, 24 авг 2004.

  1. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Считал в память текстовый файл, разделение строк - 0D0A (CrLf). Подсчитал количество строк. Затем загнал файл в Word, а он показывает мне больше строк, чем я посчитал. Это я обмишурился, или Word? Подскажите, плиз. На маленьком файле вручную посчитал - совпадает, на большом - 240000 строк, Word показал 270000. А вручную подсчитать слабо. Гляньте опытным глазом кто-нить, пожалуйста.
    Код (Text):
    1.     push esi
    2.     xor ebx,ebx
    3.     xor edx,edx
    4.     mov ecx,FileSize    ; размер     
    5.     mov dx,0A0Dh        ; разделитель
    6.     mov esi,ArrAddr     ; начало
    7.     push esi            ; в стек, для последующего использования
    8. @Loop:
    9.     cmp dx,word ptr[esi]
    10.     jne @F
    11.     inc ebx
    12.     push esi            ; адрес каждого разделителя - в стек
    13.     @@:
    14.     inc esi
    15.     dec ecx
    16.     jnz @Loop
    17.     shl ebx,2           ; по количеству строк * 4 получаю кусок памяти
    18.     mov PtrTblSize,ebx
    19.     push PAGE_READWRITE
    20.     push MEM_COMMIT
    21.     push ebx
    22.     push NULL
    23.     Call VirtualAlloc
    24.     mov PointerTbl,eax    ; под таблицу указателей
    25. @@: pop dword ptr[eax+ebx]; и заполняю эту таблицу
    26.     sub ebx,4             ; доставая указатели из стека
    27.     jnz @B
    28.     pop dword ptr[eax+ebx]; и в конце - указатель на начало
    29.     pop esi
    30.  




    Вот примерно так. И ещё 2 вопроса:

    1. Можно ли ускорить работу цикла по поиску разделителей и вообще это методически правильный метод составления таблицы указателей?

    2. Насколько нехорошо, что я засунул в стек 240000 двордов? В принципе, засунул и тут же высунул. Нигде не таскаю за собой. Отрабатывает довольно быстро, но всё-таки...



    Посоветуйте что-нибудь.
     
  2. pas

    pas New Member

    Публикаций:
    0
    Регистрация:
    18 апр 2003
    Сообщения:
    330
    Адрес:
    Russia
    Вы считаете разделители, которые в ДОСе считаются разделителем строки, а в ВОРДе это конец параграфа. Возможно некоторые ДОС-овские строки не помещаются в ВОРД-овскую строку и переносятся на следующую строку. Вероятно так.
     
  3. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    cresta

    Уж не собираешься ли ты сортировать файл по строкам?

    Ты знаешь, что в ntfs файлы могут иметь размер больше, чем 2^32-1?

    Посмотри реализацию функции memchr, например, в VC crt, файл memchr.asm.
     
  4. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    pas прав - Word выдает "свое" количество строк, зависящее от установок параметров страницы.



    Еще возможные источники ошибки:

    1) В "правильном" текстовом файле разделителем строк является сочетание 0D0A. Однако в самопальных файлах могут присутствовать и одиночные символы 0D или 0A, которые также считаются разделителем строки. Поэтому для надежности следует проверять три варианта: 0D, 0A и 0D0A (в частности такой анализ делается в TStrings.SetText от Borland)

    2) В DOS-файлах может встречаться символ раздела страницы (код 0C,если не ошибаюсь). Считать его отдельной строкой или нет - вопрос.



    Что касается стека, то это тоже вопрос надежности. Надежнее, конечно, не возиться со стеком, а сразу работать с VirtualAlloc (MEM_RESERVE + MEM_COMMIT по необходимости) .

    Другой известный вариант - SparseArray (используется например в компоненте TStringGrid от Borland). Суть подхода в том, что указатели хранятся не в виде единого массива, а виде массива указателей на массивы (блоки) фиксированного размера (обычно кратного степени 2 для упрощения преобразования линейного индекса в номер блока и индекс элемента внутри блока).
     
  5. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    pas



    Вполне ясно.



    leo



    В файле только 0d0a, это проверено.



    Тогда мне дважды надо пройти файл, один раз - получить кол-во строк (указателей), чтобы по этому кол-ву выделить память, и второй раз - собственно заполнение этой памяти адресами.





    Какие это даёт преимущества? Я должен пройти через массив в любом случае, и раз я это делаю, то заодно и сохраняю адреса строк. SparseArray тоже не сможет обойтись без первого прохода. И каков смысл сначала разложить адреса на составляющие (номер блока+смещение внутри блока), а затем выполнять обратное преобразование? В чём здесь фишка, я не понял. Я буду адресовать непосредственно как dword ptr[eax], а в случае с блоками надо ещё и вычислить значение, которое загрузить в eax.



    q_q



    Ну да, это моё любимое занятие :) Да и ntfs меня не пугает, я его стороной обойду.







    Не нашёл я у себя файла memchr.asm.
     
  6. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    cresta

    да, это моё любимое занятие

    Если в файле строки не слишком длинные, то запускай sort.exe и используй ее результат.



    Не нашёл я у себя файла memchr.asm

    см. аттачь.



    Насколько нехорошо, что я засунул в стек 240000 двордов?

    Зачем? Тебе необходимо рисовать прогресс сортировки?



    Я делал так:

    Зарезервировал память для N адресов строк - массив адресов отсортированных строк. Адрес первой строки помещаю в начало массива. Читаю очередную строчку и методом половинного деления беру из массива адрес строки для сравнения с очередной. Как только нашел место очередной строки в массиве, то вставляю ее, все которые больше двигаются вниз. Разумеется, необходимо проверять не пора ли увеличить размер массива. Когда прочитаны все строки, то записываю результирующий файл согласно массиву.



    Ты используешь mmf?

    [​IMG] _1988072419__memchr.rar
     
  7. masquer

    masquer wasm.ru

    Публикаций:
    0
    Регистрация:
    13 сен 2002
    Сообщения:
    890
    Адрес:
    Николаев
    в Word-е вообще 0Dh - это окончание параграфа, 0Ah для текста ничего не означает и пропускается





    От каких это, можно уточнить?
     
  8. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    masquer

    Левое и правое поля.
     
  9. pas

    pas New Member

    Публикаций:
    0
    Регистрация:
    18 апр 2003
    Сообщения:
    330
    Адрес:
    Russia
    masquer

    ПОртрет Ландшафт,размер шрифта. ИМХО.
     
  10. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    pas

    размер шрифта

    Это не параметр страницы.
     
  11. pas

    pas New Member

    Публикаций:
    0
    Регистрация:
    18 апр 2003
    Сообщения:
    330
    Адрес:
    Russia
    q_q



    Согласен, но тоже влияет на количество символов в строке.
     
  12. masquer

    masquer wasm.ru

    Публикаций:
    0
    Регистрация:
    13 сен 2002
    Сообщения:
    890
    Адрес:
    Николаев
    q_q, pas

    Ну, хорошо, есть у меня, например, длинное предложение (одно на абзац, например), как ваши установки могут привести к изменению этого кол-ва. Честно, перепробовал все (ну, кроме размера шрифта :) ) кол-во абзацев ну никак не поменялось. И не должно оно меняться он _установок листа_.



    По теме, загнал большой текстовый файл в ворд, кол-во символов абзаца (0d) в ворде (физическом файле) совпало с кол-вом 0d0a в текстовом файле, но оказалось немного больше того, как показала Статистика в ворде (не знаю, как оно там меряет).



    Хе-хе, между кол-вом символов в строке и кол-вом абзацев есть некоторая разница (в ворде, ес-но)
     
  13. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    masquer

    длинное предложение (одно на абзац, например) ... перепробовал все ... кол-во абзацев ну никак не поменялось

    Не путай количество абзацев и количество строк.



    [​IMG] _373254595__count.gif
     
  14. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    cresta >




    imho ничего страшного в этом нет, но нужно знать несколько моментов:

    http://www.wasm.ru/forum/index.php?action=vthread&forum=4&topic=5573

    По ссылкам приведённым Four-F можно много интересного найти :derisive:



    По поводу CrLf может попробовать, например, OpenOffece или другой редактор, Word imho глючный :)
     
  15. masquer

    masquer wasm.ru

    Публикаций:
    0
    Регистрация:
    13 сен 2002
    Сообщения:
    890
    Адрес:
    Николаев


    а при чем здесь кол-во строк (а понял, это автор топика внес)? Если есть текстовый файл и в нем есть 0d0a, то в ворде это сконвертнется в 0d и будет считаться за абзац, а не строку
     
  16. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    masquer

    при чем здесь кол-во строк

    Я так думаю, что автор смотрит именно на количество строк, не обращая внимания на количество абзацев.
     
  17. masquer

    masquer wasm.ru

    Публикаций:
    0
    Регистрация:
    13 сен 2002
    Сообщения:
    890
    Адрес:
    Николаев


    Ну, для того, чтобы кол-во _строк_ подсчитать нужно действительно знать и размеры листа, и margins, и кучу параметров шрифта и пр. :)

    Если для текста, то хотя бы органичитель для длины строки. А так 0d0a это и будет абзац.
     
  18. leo

    leo Active Member

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

    Суть использования VirtualAlloc или SparseArray состоит именно в том, что не нужно заранее знать размер результирующего массива.

    Например, при использовании VirtualAlloc с флагом MEM_RESERVE можно сразу зарезервировать адресное пространство равное размеру файла (ну или 1/2,1/3 - хотя при резервировании это большой роли, не играет если речь не идет о гигабайтах,т.к. при этом никакой физической памяти не выделяется). Далее возможны два подхода. "Классический" - выделять память, используя MEM_COMMIT, блоками фиксированного размера: если после очередного inc индекс в ebx превышает размер выделенной памяти - выделяешь следующий блок по адресу BaseAddr+(ebx-1)*4. Другой подход - менее "элегантный" - сразу выделить достаточный размер памяти MEM_COMMIT. Если внимательно читать Win32.hlp, то увидим, что "The system initializes and loads each committed page into physical memory only at the first attempt to read or write to that page", т.е. пока мы не добрались до какой-то commited страницы - физической памяти она не занимает. В этом случае по завершении формирования массива делаем VirtalFree, освобождая лишнюю память.



    Суть использования SparseArray, также состоит в том, что мы пишем указатели в текущий блок, а когда он заполняется - выделяем новый и пишем в него. Ссылки на начало блоков хранятся в отдельном массиве. Конечно такой метод алгоритмически сложнее, чем VirtualAlloc. Но в отличие от последнего он не требует размещения всех блоков по последовательным адресам в памяти (sparse = разбросанный). Каждый блок может распределяться GlobalAlloc и лежать по произвольным адресам - где системе будет угодно. Конечно если ты загрузил массив в одной процедуре и все, то SparseArray никчему. А вот если к существующему массиву нужно добавить новый массив строк, то при обычном подходе нужно делать ReAlloc и переписвать кучу указателей, а в случае SparseArray - только указатели на блоки.
     
  19. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    q_q







    Строки произвольной длины, до 2Гб. Если бы были фиксированной длины, то начало строки можно было бы просто рассчитать. А в случае переменной длины расчёт невозможен, поэтому составляю массив указателей на начала строк. Чтобы потом сортировать по указателям.







    Тогда надо будет немалое количество раз заниматься пересылками строк. С учетом возможной длины строк и их количества около 300 000 - Это время. А с указателями я исходный массив строк не трогаю, только сами указатели переставляю. После сортировки беру последовательно указатели и соответствующие им строки пихаю в новый кусок памяти, который выгружаю в файл. Т.е. пересылка строк - один раз, уже после сортировки. А может и этой пересылки строк можно будет избежать, если WriteFile позволит делать append. Надо будет глянуть.







    Не понял, Mail Message File что-ли?



    За аттач спасибо.





    S_T_A_S_







    Единственная операция с загруженым стеком - вызов VirtualAlloc. И тут же разгрузил его. Мне кажется, коллизий типа исключений быть не должно. Неоднократно пробовал - вроде проблем не было.
     
  20. leo

    leo Active Member

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

    На больших массивах метод сортировки путем вставки очередной строки работает медленно из-за большого числа бестолковых пересылок. Гораздо лучше в плане быстродействия загрузить строки как есть, а затем использовать известный алгоритм QuickSort (опять же ссылаюсь на Borland Delphi или C Builder: TStringList.QuickSort). Работает гораздо быстрее.