LINUX.ORG.RU

OpenOffice BASIC: cкорость чтения из текстового файла

 , , ,


0

2

Есть макрос для OpenOffice Calc, который

  • просит пользователя указать директорию с которой будет дальше работать;
  • получает список директорий из указанной;
  • в каждой из этих (обычно 10-30 шт) директорий открывает текстовый файл примерно в 18-20 тыс. строк каждый с результатами расчёта, просматривает его в поисках определённых полей и чтения соответствующих им значений;
  • записывает значения найденных полей в ячейки активного листа.

Время отработки макроса порядка 15-30 секунд и все они тратятся на чтение из файла. Сегодня переписал этот макрос на MS VBA для Excel 2003, изменив только функцию выбора директории и некоторые переменные (так как отличались методы указания активной ячейки). Функции чтения из файла остались такими же. Макрос при этом отрабатывает практически мгновенно. То есть на просмотр того же количества строк в файлах уходит не больше 2 секунд. Почему чтение из файла в OO Basic может работать так медленно?

Есть ли у кого опыт написания макросов для OpenOffice на Python, насколько быстрее написанное на нём чтение из таких файлов будет отрабатывать быстрее?

★★★★★

Можно увидеть таинственный код чтения из файла?
Уверены, что проблема в чтении из файла, а не в записи на лист?

bormant ★★★★★ ()
Ответ на: комментарий от bormant

Скорее всего завтра вечером примеры скину. Но вообще используется то же самое, что описано здесь (функции открытия и чтения из «Reading Text Files» в конце). Сначала использовал другую функцию, через UNO-Service, которая может задавать кодировку при чтении, но изменение на вышеуказанную никак не изменило скорость.
Уверен, что проблема в чтении из файла, проверял расстановкой breakpoints внутри проверок условий искомых полей, где в случае обнаружения нужного поля и проходила запись в ячейки. В ячейки как раз не так много пишется данных, не больше, чем на одну страницу. Код макросов (то есть все проверки) идентичный для calc и excel.

grem ★★★★★ ()
Последнее исправление: grem (всего исправлений: 1)
Ответ на: комментарий от bormant

Сначала использовал функции открытия и чтения как здесь, через «intextstream», но разницы никакой в скорости не было.

grem ★★★★★ ()
Ответ на: комментарий от grem

В качестве проверки: останутся ли тормоза если закомментировать всё, кроме собственно чтения (проверки в прочитанном, формирование данных, их запись на лист)?

bormant ★★★★★ ()

Если у файла вменяемая структура (типа csv), то лучше открывать в отдельном листе штатным импортёром и потом гулять по ячейкам скриптом.

ziemin ★★ ()
Ответ на: комментарий от ziemin

Кстати, да, в бытность форума на i-rs.ru такой подход проверялся и сильно выигрывал в скорости исполнения у построчного чтения в OOO Basic...

bormant ★★★★★ ()
Ответ на: комментарий от bormant

Тут первый вариант чтения из файла, но по ссылке выше через «Line Input #FileNo, CurrentLine» не был быстрее. Оставил вот такой кусок с выводом времени в две ячейки до него и после:

  For j = 0 to count-1 
  	sFilename = sMyDir + sDirList(j) + "\" + "my_file.out"
  	If FileExists(sFilename) Then
      ' Opening file to read in UTF-8
      encoding = "UTF-8"  ' кодировка входного файла
      ' names of codepage are described here:
      ' http://www.iana.org/assignments/character-sets/character-sets.xhtml

      fileaccess = createUnoService ("com.sun.star.ucb.SimpleFileAccess")
      intextstream = createUnoService ("com.sun.star.io.TextInputStream")
      intextstream.setEncoding( encoding )
      intext = fileaccess.openFileRead( sFileName )
      intextstream.setInputStream( intext )
      
      ' Check of reaching of the End of File
      Do While not intextstream.isEOF
        ' Reading line from File and parsing
        CurrentLine = intextstream.readLine     
      Loop  
      
      REM closing file
      intextstream.closeInput()
  	End if
  Next j
В этот раз чтение 13 файлов (по одному из 13 директорий) по 30-40 тыс. строк каждый. Время выполнения этого куска 17 секунд. К сожалению, на csv файл не похож, и большую часть данных мне на самом деле нужно просто пропустить, так как нужные находятся почти в самом начале, первые 60 строк (начальные условия) и самом конце, последние 30 строк (результат). Промежуточные данные в виде результатов в разные моменты времени пока не нужны. Просто удивило, что «один и тот же» код выполняется с таким отличием по времени в calc и excel. О_о

grem ★★★★★ ()
Ответ на: комментарий от bormant

Да, если бы это был csv, то использование встроенных методов по обработке листа как единого диапазона данных действительно работало б быстрее, чем если запихивать все ячейки в массив и работать с ним. То же самое касается и методов сортировки в ячейках, лучше использовать строенные функции сортировки.

grem ★★★★★ ()
Ответ на: комментарий от ziemin

Попробую, всё-таки, как-нибудь переписать открытие и поиск словно это файл .csv. Как открыть произвольный файл в calc уже нашёл (делая его скрытым). Осталось дописать функцию поиска в открывшемся. Если получится, отпишусь о результатах.

grem ★★★★★ ()
Ответ на: комментарий от grem

Перед чтением запусти внешний скрипт

Shell("bash -c "" echo hello > tmp.txt "" ")

пусть выкусит нужные тебе строки и максимально их подготовит для libre/open-office

anonymous ()
Ответ на: комментарий от bormant

Перепроверил ещё раз на том же компьютере с которого всё началось, а не на другом, закомментировав всё кроме открытия файла (16 файлов в цикле), чтения из него и закрытия файла методами через примерно такой код:

Open Filename For Input As FileNo
  Do While not eof(FileNo)
    Line Input #FileNo, CurrentLine   
  Loop
Close #FileNo 

вывод времени в ячейки между началом этого куска и его конца - 2 секунды, попробовал врубить поиск одной строки в прочитанный строке - время увеличилось до 4 секунд; обработка строки с найденным соответствием и записью в лист не повлияла на время. Выполнение всего макроса - 13 секунд, ~11 из которых - поиск совпадений в прочитанной строке и обработка найденного соответствия.

Переписал макрос на скрытое (чтобы окошки не появлялись) открытие файлов с данными как csv-файлов следующей функцией (хотя можно и как текстовый во writer попробовать):

Function openSpreadSheet (oFile as String) as Object
	Dim oUrl as String
	Dim oPropertyValue(2) As New com.sun.star.beans.PropertyValue	
	
	If fileExists (oFile) Then
		oUrl = convertToUrl(oFile)
	Else
		oUrl = "private:factory/scalc"
	End If
	
	oPropertyValue(0).Name = "FilterOptions"
	oPropertyValue(0).Value = "," 'используем запятую в качестве разделителя полей
	oPropertyValue(1).Name = "FilterName"
	oPropertyValue(1).Value = "Text - txt - csv (StarCalc)"
	oPropertyValue(2).Name = "Hidden"
	oPropertyValue(2).Value = True
	openSpreadSheet = starDeskTop.loadComponentFromURL(oUrl, "_blank", 0, oPropertyValue)	
End Function

Поиск в открытом листе осуществил процедурой

Sub SearchByCond(oSheet as Object, searchStr as String, MyRes() as String) as String
	oSearch = oSheet.createSearchDescriptor()
    oSearch.setSearchString(searchStr)
	oFound = oSheet.findFirst(oSearch)
	
	i = 0
	ReDim MyRes(i) as String
	Do Until IsNull(oFound)
		MyRes(i) = oFound.String
		ReDim Preserve MyRes(i+1) as String 
		oFound = oSheet.findNext(oFound, oSearch)
		i = i + 1
	Loop
End Sub

Общее время выполнения ускорилось на 2 секунды и вместо 13 секунд макрос стал выполняться за 11. Но если убрать всё, кроме открытия файлов, то такое «скрытое чтение» занимает уже ~5 секунд. Остальные ~6 секунд тратятся на 16 заходов поиска и обработки строк. Изменение размерности массива в процедуре SearchByCond не должно влиять, так как цикл «Do Until IsNull(oFound)» при чтении одного файла выполняется 2 раза с количеством входов не больше 2-4.

Похоже, что быстрее не отработает.

grem ★★★★★ ()
Ответ на: комментарий от grem

А как объявлена CurrentLine? Если она без явного указания типа (то есть Variant), попробуйте сравнить с

Dim CurrentLine$
    ...
    Line Input #FileNo, CurrentLine$
Dim CurrentLine as String
Dim CurrentLine as String * 127
тут сообразно максимальной длине строки с разумным запасом. Будут ли подвижки?

Можно попробовать сэкономить ReDim-ы строкового массива:

n=100
i=0
ReDim MyRes(n) as String
...
i = i + 1
if i > n then
  n = n + 100
  ReDim Preserve MyRes(n) as String 
end if
MyRes(i) = ...

bormant ★★★★★ ()
Последнее исправление: bormant (всего исправлений: 1)
Ответ на: комментарий от bormant

CurrentLine задан явным образом как «Dim CurrentLine as String». Емнип, «Dim CurrentLine$» - значит то же самое, проверил, что никак не влияет на время.

Задавал ещё «Dim CurrentLine as String * 80», никак не отразилось. «Dim CurrentLine as String * 10» никак не повлияло на длину считываемой строки.

«Sub SearchByCond» переписал, чтобы он внутри не издевался над массивом и просто ввёл счётчик, передаваемый наружу - тоже никак не повлияло.

Раз не получилось, ну и ладго, зато файлы в фоне научился обрабатывать :)

grem ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.