Что нового

Отследить изменение текстового файла и передать новые строки в переменную.

hikki

Продвинутый
Сообщения
233
Репутация
99
Есть текстовый файл, лог чата игры, файл постоянно пополняется новыми строками, надо отследить изменение файла и если в файле появилось что новое то передавать новые строки в переменную. Подскажите в куда копать?
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,485
hikki [?]
надо отследить изменение файла и если в файле появилось что новое то передавать новые строки в переменную.
Прочитать файл в переменную при запуске скрипта, далее в цикле проверять новое содержимое файла, и/или его размер, и/или последнее время изменения, или MD5 сумму. При несоответствий определить различия строк.

Эти топики помогут в задаче:
Библиотека для сравнения строк/чисел/1D и 2D массивов
Сравнение файлов по времени последнего изменения
Сравнить 2 текстовых файла.


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

Вот так примерно:

MD5.au3

Код:
#include "MD5.au3"

$sFile = "File.log"
$sRead_Origin = FileRead($sFile)
$sMD5_Origin = _MD5_MD5ForString($sRead_Origin)

$sNew_Lines = ""

While 1
	Sleep(1000)
	
	$sRead_Current = FileRead($sFile)
	$sMD5_Current = _MD5_MD5ForString($sRead_Current)
	
	If $sMD5_Current <> $sMD5_Origin Then
		$sCompare = _StringCompareEx($sRead_Origin, $sRead_Current)
		$iLine_Number = @extended
		
		$sMD5_Origin = $sMD5_Current
		$sRead_Origin = $sRead_Current
		
		If $sCompare <> "" Then
			$sNew_Lines &= $sCompare & @CRLF
			MsgBox(64, 'Title', $sNew_Lines)
		Else
			;No new lines, the difference is in the existing lines
		EndIf
	EndIf
WEnd

Func _StringCompareEx($sCompare_FirstStr, $sCompare_SecondStr)
    Local $sRead_FirstStr = $sCompare_FirstStr
    Local $sRead_SecondStr = $sCompare_SecondStr

    If $sRead_FirstStr == $sRead_SecondStr Then Return ""
    
    Local $aFirstStr_Array = StringSplit(StringStripCR($sRead_FirstStr), @LF)
    Local $aSecondStr_Array = StringSplit(StringStripCR($sRead_SecondStr), @LF)
    
    Local $iFirstStr_Ubound = UBound($aFirstStr_Array)-1
    Local $iSecondStr_Ubound = UBound($aSecondStr_Array)-1
    
    Local $iBiggest_Ubound = $iFirstStr_Ubound
    If $iSecondStr_Ubound > $iFirstStr_Ubound Then $iBiggest_Ubound = $iSecondStr_Ubound
    
    Local $sRet = ""
    
    For $i = 1 To $iBiggest_Ubound
        If $i > $iFirstStr_Ubound And $aSecondStr_Array[$i] <> "" Then
			$sRet &= "Line Added (#" & $i & "): " & $aSecondStr_Array[$i] & @CRLF
        EndIf
    Next
    
	If $sRet = "" Then Return SetError(1, 0, "")
	Return StringStripWS($sRet, 3)
EndFunc
 
Автор
H

hikki

Продвинутый
Сообщения
233
Репутация
99
Почти то что надо, код был бы рабочим еслибы в это время файл не был открытым на запись игрой. Тоесть скрипт висит в трее до выхода из игры после этого показывает все изменения.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
hikki [?]
Есть текстовый файл, лог чата игры, файл постоянно пополняется новыми строками, надо отследить изменение файла и если в файле появилось что новое то передавать новые строки в переменную.

Исходя из этого условия и учитывая :

[?]
код был бы рабочим еслибы в это время файл не был открытым на запись игрой. Тоесть скрипт висит в трее до выхода из игры после этого показывает все изменения

Постоянно контролировать только кол-во строк.
Только при выявлении его изменения :
1. Кратковременно открыть файл для чтения
2. Прочитать изменения
3. Закрыть файл

Код:
#include <file.au3>

$sFilePath=@ScriptDir &'\File.log'
Global $iDefCount=_FileCountLines($sFilePath) 

HotKeySet("^s", "SaveLog")

ReadLog()

Func SaveLog()
   $iRandom=Random ( 1, 4,1) 
   $iC=_FileCountLines($sFilePath) 
   For $i=1 To $iRandom
	  $iProc=_FileWriteLog($sFilePath,"Запись " & $iC+$i )
   Next
EndFunc

Func ReadLog()
   While 1
      $iCount=_FileCountLines($sFilePath)
      If $iCount > $iDefCount Then 
         Dim $sText=''
		 $hFile = FileOpen($sFilePath, 0)
         $ii=0
		 For $j=$iDefCount+1 To $iCount
			$ii+=1
			$sLine=FileReadLine ( $hFile , $iDefCount+$ii ) 
			$sText &= $sLine & @CRLF
		 Next
		 FileClose($hFile)
		; MsgBox(0,'Появилась новая запись',$sText,1)
         ConsoleWrite($sText)
      EndIf
      $iDefCount=$iCount
      Sleep(10)
   WEnd
 
EndFunc
 
Автор
H

hikki

Продвинутый
Сообщения
233
Репутация
99
Файл открыт не скриптом, а игрой, так что я закрыть его никак не могу. Попробую копировать файл, мож что получится.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,485
hikki [?]
код был бы рабочим еслибы в это время файл не был открытым на запись игрой
А если получать к нему доступ через WinAPI?

Код:
#include <WinAPI.au3>

...

$sRead_Origin = _FileReadEx($sFile)

...

Func _FileReadEx($sFile)
	Local $hFile, $sRet, $nBytes, $tBuffer
	
	$iFileSize = FileGetSize($sFile)
	$tBuffer = DllStructCreate("byte[" & $iFileSize & "]")
	$hFile = _WinAPI_CreateFile($sFile, 2, 2, 6)
	_WinAPI_ReadFile($hFile, DllStructGetPtr($tBuffer), $iFileSize, $nBytes)
	
	$sRet = BinaryToString(DllStructGetData($tBuffer, 1))
	_WinAPI_CloseHandle($hFile)
	
	Return $sRet
EndFunc


везде вместо FileRead использовать _FileReadEx.
 
Автор
H

hikki

Продвинутый
Сообщения
233
Репутация
99
Блин, анекдот какойто, теперь скрипт открывает файл, игра не может записать лог в него и создает новый файл.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
hikki [?]
Блин, анекдот какойто, теперь скрипт открывает файл, игра не может записать лог в него и создает новый файл.

Конечно при постоянном контроле текста файл же постоянно открывается для чтения.
Все же постоянно контролировать надо кол-во строк или размер файла - это не требует открытия файла
 
Автор
H

hikki

Продвинутый
Сообщения
233
Репутация
99
Кое что обрисовалось. Вроде с копированием и с помощью функции _StringCompareEx из примера CreatoR'а заработало.

Код:
Dim $chat_old, $chat_new, $sNew_Lines

FileCopy("c:\log\log.txt", "C:\test\", 1)
$chat_old = FileRead("C:\test\log.txt")


While 1
    While FileGetTime ( "c:\log\log.txt",0, 1)=FileGetTime ( "C:\test\log.txt",0, 1);время изменения в строчном виде YYYYMMDDHHMMSS
        Sleep(1000)
        WEnd

FileCopy("c:\log\log.txt", "C:\test\new\", 1)
$chat_new=FileRead("C:\test\new\log.txt")
$sCompare = _StringCompareEx($chat_old, $chat_new)


If $sCompare <> "" Then
    $sNew_Lines &= $sCompare & @CRLF
    MsgBox(64, 'Title', $sNew_Lines)
    FileMove("C:\test\new\log.txt", "C:\test\", 1)
    $chat_old = FileRead("C:\test\log.txt")
    EndIf
WEnd

Func _StringCompareEx($sCompare_FirstStr, $sCompare_SecondStr)
    Local $sRead_FirstStr = $sCompare_FirstStr
    Local $sRead_SecondStr = $sCompare_SecondStr

    If $sRead_FirstStr == $sRead_SecondStr Then Return ""

    Local $aFirstStr_Array = StringSplit(StringStripCR($sRead_FirstStr), @LF)
    Local $aSecondStr_Array = StringSplit(StringStripCR($sRead_SecondStr), @LF)

    Local $iFirstStr_Ubound = UBound($aFirstStr_Array)-1
    Local $iSecondStr_Ubound = UBound($aSecondStr_Array)-1

    Local $iBiggest_Ubound = $iFirstStr_Ubound
    If $iSecondStr_Ubound > $iFirstStr_Ubound Then $iBiggest_Ubound = $iSecondStr_Ubound

    Local $sRet = ""

    For $i = 1 To $iBiggest_Ubound
        If $i > $iFirstStr_Ubound And $aSecondStr_Array[$i] <> "" Then
            $sRet &= "Line Added (#" & $i & "): " & $aSecondStr_Array[$i] & @CRLF
        EndIf
    Next

    If $sRet = "" Then Return SetError(1, 0, "")
    Return StringStripWS($sRet, 3)
EndFunc


но почемуто сообщения показывает через одно, те я в чате пишу 1 интер, 2 интер, 3 интер, 4 интер, то скрипт на 1 не реагирует, показывает 2 и 4. помогите допилить
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Попробовал и так ине нашел причину. действительно ловит через раз.
Попробовал по своему принципу отлова только с использованием копирования.
Вроде ловит нормально все:
Код:
#include <file.au3>

$sFilePath=@ScriptDir &'\File.log'
Global $iDefCount=_FileCountLines($sFilePath) 

HotKeySet("^s", "SaveLog")

ReadLog()

Func SaveLog()
   $iRandom=Random ( 1, 4,1) 
   $iC=_FileCountLines($sFilePath) 
   For $i=1 To $iRandom
      $iProc=_FileWriteLog($sFilePath,"Запись " & $iC+$i )
   Next
EndFunc

Func ReadLog()
   While 1
      $iCount=_FileCountLines($sFilePath)
      If $iCount > $iDefCount Then 
		 $iProc=FileCopy($sFilePath, @ScriptDir &'\new\*.*', 1)
		 $hFile = FileOpen(@ScriptDir &'\new\File.log', 0)
         Dim $sText='', $ii=0
		 For $j=$iDefCount+1 To $iCount
            $ii+=1
            $sLine=FileReadLine ( $hFile , $iDefCount+$ii ) 
            $sText &= $sLine & @CRLF
         Next
         FileClose($hFile)
		 ConsoleWrite($sText & @LF)
	  EndIf
      $iDefCount=$iCount
      Sleep(10)
   WEnd
 
EndFunc


Попробуй на игровом файле
 
Автор
H

hikki

Продвинутый
Сообщения
233
Репутация
99
Спасибо всем помогавшим, работает :IL_AutoIt_1: .

Код:
#include <file.au3>
$dir_log="c:\logs"
$log_name="\log.txt"


FileCopy($dir_log&$log_name, @ScriptDir, 1)
$FilePathTarg= @ScriptDir &$log_name
$lines1=_FileCountLines($FilePathTarg)

While 1
    $file_dif=""
    Select
        Case FileGetTime ($dir_log&$log_name,0, 1)<> FileGetTime ( $FilePathTarg,0, 1)
        FileCopy($dir_log&$log_name, @ScriptDir&"\new", 1)
            $lines2=_FileCountLines(@ScriptDir&"\new"&$log_name)
        for $i= $lines1 To $lines2
            $lineread=FileReadLine(@ScriptDir&"\new"&$log_name, $i)
            $file_dif&=$lineread &@CRLF
        Next
        MsgBox(64, "Сообщения", $file_dif)
    EndSelect
    Sleep(2000)
WEnd


что за дела, три раза править пришлось, убивает слеши в сообщении
 

necrozyablo

Новичок
Сообщения
10
Репутация
1
Кстати а у вас там с кодировкой проблем нет?
Ну просто вот у меня на питоне кол-во строк в файле читается sum(1 for l in open(fil2, 'r')) ну или len(open(file, 'r').readlines()) но там в логах по умолчанию кодировка USC-2 Little Endian (это разновидность utf16) и если кто то по китайски\русски что то напишет у меня всё падало.
Пришлось указывать UTF-16LE

sum(1 for l in codecs.open(path+file_name, 'r', 'UTF-16LE'))
 
Автор
H

hikki

Продвинутый
Сообщения
233
Репутация
99
Все в порядке с кодировкой, проверялось на Eve online, игровой канал MOZG, где большинство говорит на русском.

PS после небольших експериментов оказалось что вместо
Код:
FileGetTime
надо использовать
Код:
FileGetSize

почемуто на другом компе файл хоть и постоянно изменялся но в атрибутах это никак не отображалось.
 
Верх