Что нового

Как быстро разбить на массив строк большой файл, прочитанный целиком?

Suppir

Продвинутый
Сообщения
967
Репутация
62
Есть 5-мегабайтный файл, который я зачитал в переменную $file целиком с помощью FileRead.

Этот файл состоит из строк вида:

ааа=12
bbb=13
ccc=14
ddd=23
...
и так миллион строк.

Необходимо из переменной $file сделать массив вида:
$array[1][0]='aaa'
$array[1][1]='12'
$array[2][0]='bbb'
$array[2][1]='13'
и т.д.

Как это сделать быстрее всего? Желательно за 1-2 секунды, если возможно.


Добавлено:
Сообщение автоматически объединено:

Пробовал Split - возвращает @error = 1
Регулярными выражениями 5-мегабайтную строку лучше не обрабатывать.
 

dwerf

Использует ArchLinux
Сообщения
478
Репутация
219
1. С помощью StringSplit
2. Как нибудь так:
$array[1][0]= от переноса строки до знака равно
$array[1][1]= от предыдущего знака равно до следующего сброса строки
$array[2][0]= от предыдущего сброса до следующего знака...
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
StringSplit() в данном случае самая быстрая функция, однозначно используй ее.

Код:
$aData = StringSplit($File, @CRLF)


А вот переменные можно оставить в формате "ааа=12" и обрабатывать по мере надобности. Любая перетасовка больших массивов займет много времени.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Yashied
пробовал!

пишу:
Код:
$aData = StringSplit($File, @CRLF)
_ArrayDisplay($aData)


Выводит в месседжбоксе:
Код:
$aData[0]=0
$aData[1]=-1


Т.е. сплит не сработал.
 

SyDr

Сидра
Сообщения
651
Репутация
158
Странно как-то.
$aData[0] должно быть равно 1, если stringSplit не сработал.
$aData[1] - равно оригинальному параметру $File
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Завтра на работе еще раз попробую. Перебрал все варианты со StringSplit - не работало ничего. Насколько я понял, у StringSplit есть ограничение на длину строки.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
А не лучше делать Split по " = " ?
У меня так файл 2.4mB обрабатывается за 2 сек : :smile:
Код:
$sFile=@ScriptDir & "\Test.txt"   
  $hFile=FileOpen($sFile,0)
  $sText= FileRead($hFile)
  $aArray=StringSplit($sText,"=")
  ;_arraydisplay($aArray)
  MsgBox(0,"",$aArray[10])
  MsgBox(0,"",$aArray[100008])

Если с _arraydisplay($aArray) , то тогда через минуту выдается массив из 600 000 эл-тов .
Файл состоит из множества строк :
Код:
$sStr="ааа=12"
$sStr="bbb=25"
$sStr="ccc=30"
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Скорее всего у тебя в файле нет "@CRLF"
У меня с ним тоже не находит,зато находит с "@LF" за те же 2 секунды и с "@CR"
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Suppir [?]
у StringSplit есть ограничение на длину строки
Нет. Есть общее ограничение на длину строк (2,147,483,647 символов) и ячеек массива (размерность <= 64 и/или всего 16 милл. элементов).
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
gregaz сказал(а):
Скорее всего у тебя в файле нет "@CRLF"
У меня с ним тоже не находит,зато находит с "@LF" за те же 2 секунды и с "@CR"

Потому что Unicode.

Код:
$CRLFW = Chr(0) & @CR & Chr(0) & @LF



Добавлено:
Сообщение автоматически объединено:

Если очень важна скорость, то почему бы не работать непосредственно со строкой, StringInStr() никто не отменял.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Я записал файл следующим скриптом:
Код:
$s1 = 'ааа='
$s2 = 'bbb='
$s3 = 'ccc='
$s4 = 'ddd='

$hFile = FileOpen('test.txt', 2)
For $i = 1 To 1000000
	FileWrite($hFile, Eval("s" & Random(1, 4, 1)) & Random(1, 1000, 1) & @LF)
Next
FileClose($hFile)
Поучился файл размером 7,52 Мб. Следующим скриптом я разбил текст на 4 массива:
Код:
$s1 = 'ааа='
$s2 = 'bbb='
$s3 = 'ccc='
$s4 = 'ddd='

$timeout = TimerInit()
$hFile = FileOpen('test.txt', 0)
$sText = FileRead($hFile)

$a1 = StringRegExp($sText, $s1 & '(\d{1,4})', 3)
$a2 = StringRegExp($sText, $s2 & '(\d{1,4})', 3)
$a3 = StringRegExp($sText, $s3 & '(\d{1,4})', 3)
$a4 = StringRegExp($sText, $s4 & '(\d{1,4})', 3)

MsgBox(0, Round(TimerDiff($timeout) / 1000, 2), UBound($a1) & @CRLF & UBound($a2) & @CRLF & _
		UBound($a3) & @CRLF & UBound($a4))
У меня времени на это ушло ~5,5 секунд. Если делать выборку только по одному элементу, то у меня времени уходит ~1,6 секунд.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Suppir
Я успел скачать Ваш файл. :smile:
С ним у меня следующий код отрабатывает ~ за 0,5 сек.:
Код:
#include <Array.au3>
$s1 = 'ФЗ='
$s2 = 'ФКЗ='
$s1_1 = StringTrimRight($s1, 1)
$s2_1 = StringTrimRight($s2, 1)

$timeout = TimerInit()
$hFile = FileOpen('исходник.txt', 0)
$sText = FileRead($hFile)

$a1 = StringRegExp($sText, $s1 & '(\d{1,10})', 3)
$a2 = StringRegExp($sText, $s2 & '(\d{1,10})', 3)
$iUb1 = UBound($a1)
$iUb2 = UBound($a2)
If $iUb1 >= $iUb2 Then
	$z = $iUb1
Else
	$z = $iUb2
EndIf
Dim $aResult[$z][4]

For $i = 0 To $z - 1
	If $i < $iUb1 Then
		$aResult[$i][0] = $s1_1
		$aResult[$i][1] = $a1[$i]
	Else
		$aResult[$i][0] = ''
		$aResult[$i][1] = ''
	EndIf
	If $i < $iUb2 Then
		$aResult[$i][2] = $s2_1
		$aResult[$i][3] = $a2[$i]
	Else
		$aResult[$i][2] = ''
		$aResult[$i][3] = ''
	EndIf
Next

MsgBox(0, Round(TimerDiff($timeout) / 1000, 2), 'OK')
_ArrayDisplay($aResult)
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
madmasles
Ваш скрипт у меня очень быстро отрабатывает, но ведь в нем получается только 3 тысячи документов вместо 83 тыс.
Т.е. в массив нужно все строчки поместить, а не только с "ФЗ"
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Suppir
Так Вы дайте критерии выборки.

Файл, созданный первым скриптом из этого поста, обрабатывается у меня ~ 7-7,5 сек. :
Код:
$s1 = 'ааа='
$s2 = 'bbb='
$s3 = 'ccc='
$s4 = 'ddd='
$s1_1 = StringTrimRight($s1, 1)
$s2_1 = StringTrimRight($s2, 1)
$s3_1 = StringTrimRight($s3, 1)
$s4_1 = StringTrimRight($s4, 1)
$timeout = TimerInit()
$hFile = FileOpen('test.txt', 0)
$sText = FileRead($hFile)

$a1 = StringRegExp($sText, $s1 & '(\d{1,4})', 3)
$a2 = StringRegExp($sText, $s2 & '(\d{1,4})', 3)
$a3 = StringRegExp($sText, $s3 & '(\d{1,4})', 3)
$a4 = StringRegExp($sText, $s4 & '(\d{1,4})', 3)
$iUb1 = UBound($a1)
$iUb2 = UBound($a2)
$iUb3 = UBound($a3)
$iUb4 = UBound($a4)

If $iUb1 >= $iUb2 And $iUb1 >= $iUb3 And $iUb1 >= $iUb4 Then
	$z = $iUb1
ElseIf $iUb2 >= $iUb1 And $iUb2 >= $iUb3 And $iUb2 >= $iUb4 Then
	$z = $iUb2
ElseIf $iUb3 >= $iUb1 And $iUb3 >= $iUb2 And $iUb3 >= $iUb4 Then
	$z = $iUb3
ElseIf $iUb4 >= $iUb1 And $iUb4 >= $iUb2 And $iUb4 >= $iUb3 Then
	$z = $iUb4
EndIf
MsgBox(0, Round(TimerDiff($timeout) / 1000, 2), $z & @CRLF & $iUb1 & @CRLF & $iUb2 _
		 & @CRLF & $iUb3 & @CRLF & $iUb4)
$timeout_1 = TimerInit()
Dim $aResult[$z + 1][4]
$aResult[0][0] = $s1_1
$aResult[0][1] = $s2_1
$aResult[0][2] = $s3_1
$aResult[0][3] = $s4_1

For $i = 0 To $z - 1
	If $i < $iUb1 Then
		$aResult[$i + 1][0] = $a1[$i]
	EndIf
	If $i < $iUb2 Then
		$aResult[$i + 1][1] = $a2[$i]
	EndIf
	If $i < $iUb3 Then
		$aResult[$i + 1][2] = $a3[$i]
	EndIf
	If $i < $iUb4 Then
		$aResult[$i + 1][3] = $a4[$i]
	EndIf
Next

MsgBox(0, Round(TimerDiff($timeout_1) / 1000, 2), 'OK')
MsgBox(0, '', $aResult[1000][0] & @CRLF & $aResult[1000][1] & _
		@CRLF & $aResult[1000][2] & @CRLF & $aResult[1000][3])
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
madmasles
В каждой строке файла есть знак "="

Все что находится слева от знака, идет в $array[$x][0],
а все что находится справа - в $array[$x][1],
где $x - это номер строки (и номер элемента в конечном массиве).
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Suppir
У меня так достаточно быстро работает с Вашим файлом:
Код:
#include <Array.au3>

$timeout = TimerInit()
$hFile = FileOpen('исходник.txt', 0)
$sText = FileRead($hFile)

$a1 = StringRegExp($sText, '(.*?)=', 3)
$a2 = StringRegExp($sText, '=(\d{1,10})', 3)
$iUb1 = UBound($a1)
$iUb2 = UBound($a2)
If $iUb1 <> $iUb2 Then
	If $iUb1 >= $iUb2 Then
		$z = $iUb1
	Else
		$z = $iUb2
	EndIf
Else
	$z = $iUb1
EndIf

Dim $aResult[$z][2]

For $i = 0 To $z - 1
	If $i < $iUb1 Then
		$aResult[$i][0] = $a1[$i]
	EndIf
	If $i < $iUb2 Then
		$aResult[$i][1] = $a2[$i]
	EndIf
Next

MsgBox(0, Round(TimerDiff($timeout) / 1000, 2) & 'сек.', 'Обработано строк: ' & $z)
_ArrayDisplay($aResult)
 

Norm

Продвинутый
Сообщения
269
Репутация
70
Всем доброго дня

Понимаю, что сейчас, в связи с обновлением сайта, не сразу получу ответ, но всё же попробую начать здесь.
Сам я самочка, а из опыта имею только работу с MortScript, который на ранних Winows Mobile устройствах имеет такую же популярность, как и Autoite на больших PC.
С регулярными выражениями до этого знаком не был, поэтому это здесь это мой первый опыт. Нашёл книжку Дж. Фридл (465 страниц) теперь читаю и пытаюсь разобраться, что бы в будущем применять.
Но параллельно нужно и другие вещи от Autoit осваивать и свой проект реализовать, поэтому хотелось бы получить помощь в пока сложном для меня вопросе.

Это обычный CSV-файл ноторый я сначала разбиваю на строки, а потом но столбцы (разделитель TAB)
Хотелось бы получить тоже самое но за один проход StringRegExp используя флаг 4.
Но все мои попытки не увенчались успехом

Код:
$sChars = FileRead($hFile)
$str = StringRegExp ( $sChars, "([^\v]+)", 3)

For $i = 0 To UBound($str) - 1
          $aArray = StringRegExp ( $str[$i], "([^\t]+)", 3)
          $aArray[2] = '"' & $aArray[2] & '"'
          $strNew = $strNew & @CRLF & _ArrayToString($aArray,";")
         _ArrayDisplay($aArray, "флаг=4") ; функция для просмотра массива
Next
 
Последнее редактирование:

Medic84

Омега
Команда форума
Администратор
Сообщения
1,590
Репутация
341
Всем доброго дня

Понимаю, что сейчас, в связи с обновлением сайта, не сразу получу ответ, но всё же попробую начать здесь.
Сам я самочка, а из опыта имею только работу с MortScript, который на ранних Winows Mobile устройствах имеет такую же популярность, как и Autoite на больших PC.
С регулярными выражениями до этого знаком не был, поэтому это здесь это мой первый опыт. Нашёл книжку Дж. Фридл (465 страниц) теперь читаю и пытаюсь разобраться, что бы в будущем применять.
Но параллельно нужно и другие вещи от Autoit осваивать и свой проект реализовать, поэтому хотелось бы получить помощь в пока сложном для меня вопросе.
Прошу не ругаться, но я не нашел, как правильно оформить код в сообщении, поэтому пока напишу как текст.

Код:
$sChars = FileRead($hFile)
$str=StringRegExp($sChars,"([^\v]+)",3)

For $i = 0 To UBound($str) - 1
$aArray = StringRegExp($str[$i],"([^\t]+)",3)
$aArray[2] = '"' & $aArray[2] & '"'
$strNew = $strNew & @CRLF & _ArrayToString($aArray,";")
; _ArrayDisplay($aArray, "флаг=4") ; функция для просмотра массива
Next
Я вот это вот все прочитал, но так и не понял в чем вопрос.
 
Верх