Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
24-03-2008 03:38 | Комментарий к предыдущим ответам
>>> По-видимому, здесь лучше использовать TFileStream c собственным буфером
Угу. Можно так, можно BlockRead, если Pascal знаешь лучше Delphi. Именно на это я и намекал в ответе от 15-02-2008 07:20
23-03-2008 16:15 | Комментарий к предыдущим ответам
>>> Я бы все-равно в таком случае пользовался бы Readln - Writeln, это работает медленнее <...>
Хм... Не просто медленнее, а существенно медленнее. Файд в 9 мегабайт -- это 131072 строки длиной 70 символов. Сейчас забабахал тестовую программу. TStringList, в который я в цикле загнал 131072 строки, а потом сбросил все это в файл, отработал быстрее, чен я успел заметить. А тестовая процедура, которая читала этот файл построчно и тут же каждую строку безо всякой обработки сбрасывала в другой текстовый файл, работала порядка секунды. Вот сижу и думаю, что будет если файл будет побольше или компьютер помедленнее.
>>> нужно превратить в подобие CSV
Я бы все-равно в таком случае пользовался бы Readln - Writeln, это работает медленнее, чем TStringList.LoadFromFile, но зато не грозит никаким переполнением при сколь угодно больших файлах в пределах 2 гигабайт (надеюсь, пока до такого не дошло :-))
To Banderas
>>>Если нужно, могу порыться, поискать.
Да интересно было бы взглянуть.
Volniy.Strelok@gmail.com
З.Ы.
Если не сложно то с коментами. :)
To Бел Амор
Я думаю что читая из файла по одной строке это сужественно замедлит скорость замены.
Тем более что в каждой строке файла вхождение для замены всего одно.
А строк приблизительно 200 000.
To Geo
>>>Кстати, а что на что Вы меняете. Случайно не все пробелы на какие-нибудь подчерки?
Файл правится для того чтобы потом залить его в таблицу базы данны т.е. его нужно превратить в подобие CSV файла
Однажды мне пришлось адаптировать эту функцию под Unicode, заметил переполнения стека, харнящего позиции вхождения. Исправил проблему путем введения использования блоков динамической памяти перед тем исчерпается стек.
15-02-2008 15:21 | Комментарий к предыдущим ответам
Похоже, я угадал ;-)
Да-да-да, я уже написал свои ответы, а потом увидел еще раз, что у вас, Geo, написано про это. Так что, читай и пиши я внимательнее, м.б. написал бы вместо: В стек там записываются все позиции вхождений искомой подстроки в текст. -
В стек там записываются все позиции вхождений искомой подстроки в текст, как уже написал Geo.
)))
15-02-2008 14:52 | Комментарий к предыдущим ответам
>>> Сейчас, прочитав это обсуждение, попробовал в том же файле заменить русское "о" на "а". "о" встречается 842 585 раз и я получил stack overflow.
Похоже, я угадал ;-)
15-02-2008 13:29 | Комментарий к предыдущим ответам
P.S.Понятно, что какая-то часть стека занимается, когда внутри функции StrReplace вызываются другие функции (MovMem например), но поскольку рекурсии и многоуровневых вызовов нет, то это некритично, насколько я понимаю. Выполнилась внутренняя функция - стек снова освободился.
15-02-2008 13:25 | Комментарий к предыдущим ответам
Коллеги, в функции StrReplace рекурсии нет. В стек там записываются все позиции вхождений искомой подстроки в текст. По умолчанию максимальный размер стека в программе Дельфи - $100000=1048576. Позиция вхождения - целое число, т.е. занимает 4 байта. Соответственно, если число вхождений подстроки больше, чем 1048576/4=262144, то стек переполнится. В этом - ограничение функции.
Я сегодня ответил автору вопроса на вопрос в комментариях к статье. Там он не написал, что ошибка - переполнение стека. Перед ответом я проверил функцию на большом файле. Взял стихотворения Пушкина (600 Кб, у меня лежит скачанный с библиотеки Машкова) и последователльным применением операции
copy a.txt+b.txt c.txt создал файл размером 12.6 Мб.
Я пробовал заменить тег параграфа (<p>). Он встречается в файле 79 655 раз и отлично заменился на что-то другое.
Сейчас, прочитав это обсуждение, попробовал в том же файле заменить русское "о" на "а". "о" встречается 842 585 раз и я получил stack overflow. Потом увеличил в опциях проекта стек в 10 раз и всё быстро заменилось.
777:
Стек переполняется из-за рекурсивных вызовов ф-й внутри StrReplace.
Полностью согласен, что столь длинные строки - нонсенс.
Заглянул мельком в указанную статью - StrReplace позиционируется именно как замена StringReplace для случая очень больших строк с очень частым вхождением заменяемого фрагмента. В этом случае использование рекурсии на кажное вхождение - ещё больший нонсенс, чем строки в 9 Мб... Код функции я не смотрел, так что могу в чём-то и ошибаться, но, думаю, не очень сильно...
Geo:
Повторяю цитату: "Заставь кое-кого молиться...". Далее по тексту.
После совета в такой форме человек начнет строчки из файла по одной читать ;-)
За него это уже делает TStringList...
Geo:
Читайте из файла все же блоками, но не такими большими. Особое внимание уделите тому, чтобы заменяемая строка не оказалась разорвана: часть в одном блоке, часть в другом. Иначе она не будет заменена.
Если в файле хранится чистый текст, в котором присутсвуют переводы строк, то это уже сделано, причём возможность разрывов изначально исключена...
На мой взгляд, одно из возможных решений - это читать, как и сейчас, одним махом в TStringList, но замены производить не во всём тексте сразу, а построчно, благо разбиение на строки изначально любезно производит TStringList. При этом, кстати, нет необходимости использовать самопальные функции, т.к. с небольшими строками замечательно справятся и родные функции Delphi...
P.S. Обращаю особое внимание на то, что это должен быть именно чистый текст. Наличие в файле символов #0 в случае применения TStringList принесёт большие проблемы... Отсутствие символов #0 (и некоторых других) должно быть гарантировано. К сожалению, на практике не так уж редко встречаются случаи, когда вроде бы чистый текст (отчёт в виде текстового файла) таковым не является. Если есть хоть малейшая возможность появления в файле "ненормативных" символов, настоятельно советую отказаться от использования TStringList.LoadFromFile, тем более, что в вашем первоначальном варианте его использование ничем не оправдано. P.P.S. Думаю, наиболее безопасным вариантом будет читаль файл старым добрым ReadLn по одной строке, заменять всё, что надо, и записывать в другой файл старым добрым WriteLn. Им глубоко по барабану все спецсимволы, кроме #26 (конец текстового файла)...
>>> Так надо не сразу со всем файлом работать, а как минимум построчно.
Повторяю цитату: "Заставь кое-кого молиться...". Далее по тексту.
После совета в такой форме человек начнет строчки из файла по одной читать ;-)
to Автор:
Читайте из файла все же блоками, но не такими большими. Особое внимание уделите тому, чтобы заменяемая строка не оказалась разорвана: часть в одном блоке, часть в другом. Иначе она не будет заменена.
Кстати, а что на что Вы меняете. Случайно не все пробелы на какие-нибудь подчерки? Потому что в упомянутой Вами функции, вроде бы, стек расходуется на сохранение позиций найденных вхождений. То есть, получается, что у Вас очень много вхождений искомого контекста. А даже для файла в 9 мегабайт обычное слово или фраза русского языка будут встречаться не так уж и много раз.
Тут дело не в конкретно StrReplace, а в размере стека программы, в котором вызываемые подпрограммы хранят свои внутренние переменные. Какой максимальный размер стека - можно почитать Help. Но вообще-то строка длиной в 9 миллионов символов - это нонсенс, на который явно не рассчитывали создатели функции. Придется либо резать строку, либо писать свою функцию без внутренних переменных размером с результат.
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.