Что нового

[Файловая система] Чтение файла с конца

Tuner

Новичок
Сообщения
15
Репутация
0
Ребята подскажите пожалуйста возможно ли читать файл не с начала, а с конца? Построчно или хотя бы посимвольно.
Мне нужно прочитать первую и последнюю строки в csv файле, пока что реализовано так:

Код:
$h = FileOpen($filename)
   $first = FileReadLine($h, 1)
   $last = FileReadLine($h, -1)
   FileClose($h)
   ConsoleWrite("$first = "&$first&", $last = "&$last&@CRLF)


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

Можно ли как то ускорить получение последней строки в файле?
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Как то так:

Код:
#include <FileConstants.au3>

MsgBox(64, @ScriptName, _FileReadLineFromEnd(@DesktopDir & '\2GB_File.tmp', 1))

Func _FileReadLineFromEnd($sFile, $iLine = 1, $iOffset = 100)
	Local $sLine = '', $hFile, $sRead, $iLFPos
	
	$hFile = FileOpen($sFile, 0)
	
	While 1
		FileSetPos($hFile, -$iOffset, $FILE_END)
		
		$sRead = StringStripWS(FileRead($hFile), 2)
		$iLFPos = StringInStr($sRead, @LF, 2, -$iLine)
		
		If $iLFPos Then
			If $iLine = 1 Then
				$sLine = StringMid($sRead, $iLFPos + 1)
			Else
				$sLine = StringMid($sRead, $iLFPos + 1, ($iLFPos - StringInStr($sRead, @LF, 2, 1, $iLFPos + 1) + 1) * -1)
			EndIf
			
			ExitLoop
		EndIf
		
		$iOffset += $iOffset
	WEnd
	
	FileClose($hFile)
	Return StringStripWS($sLine, 3)
EndFunc


Кстати, на основе этого можно построить функцию быстрого поиска по файлу, используя алгоритм бинарного поиска.
 
Автор
Tuner

Tuner

Новичок
Сообщения
15
Репутация
0
CreatoR сказал(а):
Как то так:
Кстати, на основе этого можно построить функцию быстрого поиска по файлу, используя алгоритм бинарного поиска.
Спасибо, отличный код! Быстрый и универсальный.
Насчет бинарного поиска, хорошая идея, примерно догадываюсь как это можно реализовать.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Tuner [?]
Вот немного изменённая версия, с проверками на ошибки, и ещё пару изменений:

Код:
#include <FileConstants.au3>

$sFile = @DesktopDir & '\2GB_File.tmp'
$iLineAtEnd = 3
$sLine = _FileReadLineFromEnd($sFile, $iLineAtEnd)

MsgBox(64, 'Line #' & $iLineAtEnd, 'From End:' & @CRLF & $sLine)

Func _FileReadLineFromEnd($sFile, $iLine = 1, $bStripWS = True)
	Local $sLine = '', $hFile, $sRead, $aSplit, $iOffset = (1024 * 2) ;2 KB
	
	$hFile = FileOpen($sFile, 0)
	If $hFile = -1 Then Return SetError(1, 0, '')
	
	While 1
		If Not FileSetPos($hFile, -$iOffset, $FILE_END) Then
			ExitLoop
		EndIf
		
		$sRead = FileRead($hFile)
		If @error <> 0 Then ExitLoop
		
		If $bStripWS Then
			$sRead = StringStripWS($sRead, 2)
		EndIf
		
		If StringInStr($sRead, @LF, 2, -$iLine) Then
			$aSplit = StringSplit($sRead, @LF)
			
			If $iLine + 1 < $aSplit[0] Then
				$sLine = $aSplit[$aSplit[0] - $iLine + 1]
			EndIf
			
			ExitLoop
		EndIf
		
		$iOffset += $iOffset
	WEnd
	
	FileClose($hFile)
	
	If $sLine = '' Then
		Return SetError(2, 0, '')
	EndIf
	
	Return StringStripWS($sLine, 3)
EndFunc


Или так (для использования как нативную альтернативу):
Код:
#include <FileConstants.au3>

$sFile = @DesktopDir & '\2GB_File.tmp'
$iLine = -1
$sLine = _FileReadLineEx($sFile, $iLine)

MsgBox(64, 'Line #' & $iLine, 'From End:' & @CRLF & $sLine)

Func _FileReadLineEx($sFile, $iLine = 1, $bStripWS = True)
	If $iLine >= 0 Then
		Return FileReadLine($sFile, $iLine)
	EndIf
	
	$iLine *= -1 ;Transform negative number to positive
	
	Local $sLine = '', $hFile, $sRead, $aSplit, $iOffset = (1024 * 2) ;2 KB
	
	$hFile = FileOpen($sFile, 0)
	If $hFile = -1 Then Return SetError(1, 0, '')
	
	While 1
		If Not FileSetPos($hFile, -$iOffset, $FILE_END) Then
			ExitLoop
		EndIf
		
		$sRead = FileRead($hFile)
		If @error <> 0 Then ExitLoop
		
		If $bStripWS Then
			$sRead = StringStripWS($sRead, 2)
		EndIf
		
		If StringInStr($sRead, @LF, 2, -$iLine) Then
			$aSplit = StringSplit($sRead, @LF)
			
			If $iLine + 1 < $aSplit[0] Then
				$sLine = $aSplit[$aSplit[0] - $iLine + 1]
			EndIf
			
			ExitLoop
		EndIf
		
		$iOffset += $iOffset
	WEnd
	
	FileClose($hFile)
	
	If $sLine = '' Then
		Return SetError(2, 0, '')
	EndIf
	
	Return StringStripWS($sLine, 3)
EndFunc
 
Верх