jeeswg's File object mini-tutorial

Put simple Tips and Tricks that are not entire Tutorials in this forum
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

jeeswg's File object mini-tutorial

31 Jul 2019, 09:33

==================================================

jeeswg's File object mini-tutorial

==================================================

CONTENTS

THE FILE OBJECT AND THE FILEOPEN FUNCTION
FILE OBJECT EXAMPLES: GENERAL
FILE OBJECT EXAMPLES: TEST READ/WRITE
FILE OBJECT EXAMPLES: WARNING: 'OPENING' A FILE CAN EMPTY ITS CONTENTS
FILE OBJECT EXAMPLES: TEST EOL OPTIONS
FILE OBJECT EXAMPLES: TEXT ENCODINGS
FILE OBJECT EXAMPLES: IDENTIFY ENCODINGS / FILETYPES
FILE OBJECT EXAMPLES: READ BINARY DATA AND REPLACE NULLS
LINE MANIPULATION
INI MANIPULATION
FILE OBJECT LINKS (TEXT)
FILE OBJECT LINKS (BINARY DATA)
FILE OBJECT LINKS (OTHER)

==================================================

THE FILE OBJECT AND THE FILEOPEN FUNCTION

LINKS

File Object - Methods & Properties | AutoHotkey
https://www.autohotkey.com/docs/objects/File.htm
FileOpen() - Syntax & Usage | AutoHotkey
https://www.autohotkey.com/docs/commands/FileOpen.htm

FUNCTIONS

FileOpen - open a file

WARNING: It's called 'FileOpen', but it can *delete* data.
WARNING: The 'w' flag *EMPTIES* the contents of any existing file (although the 'rw' flag does not do this). [Always test your scripts on a dummy file first, and when testing code, report the path to be operated on (e.g. via MsgBox) before operating on a file.]

Note: FileOpen `n option: 'Replace `r`n with `n when reading and [standalone?] `n with `r`n when writing.'
Note: FileOpen `r option: 'Replace standalone `r with `n when reading.'

METHODS

Close - close the file
RawRead/RawWrite - read/write binary data from/to the file and advance the file pointer
Read/Write - read/write characters and advance the file pointer
ReadLine/WriteLine - read/write a line of text and advance the file pointer [note: ReadLine returns a trailing line break, unless at the end of the file]
ReadXXX/WriteXXX - read/write a number and advance the file pointer [XXX: UInt, Int, Int64, Short, UShort, Char, UChar, Double, Float]
Seek - set position of file/pointer [similar to Pos/Position]
Tell (AHK v1) - get position of file pointer [similar to Pos/Position]

PROPERTIES

__Handle (AHK v1) - get a file handle [useful for DllCall]
AtEOF - true if pointer has reached the end of the file
Encoding - either UTF-8/UTF-16/CPXXX (e.g. CP1252)
Handle (AHK v2) - get a file handle [useful for DllCall]
Length - get file size in bytes
Pos/Position - get/set position of file pointer [similar to Seek/Tell]

Note: You can use the __Handle/Handle property with Winapi functions that accept hFile, see:
WinApi
https://hotkeyit.github.io/v2/docs/commands/WinApi.htm

Warning: When you open a file with a UTF-8/UTF-16 BOM, the file pointer starts at position 3 (UTF-8) or 2 (UTF-16). Use 'oFile.Pos := 0', to move the file pointer back to the start of the file. (E.g. this is important if you want to compare files, or generate a file hash, you must include the whole of the file.)

Note: Some changes may be made to the File object in AHK v2.

==================================================

FILE OBJECT EXAMPLES: GENERAL

Demonstrating File object methods/properties:

Code: Select all

;File object examples

vPath := A_AhkPath
;vPath := "C:\Program Files\AutoHotkey\AutoHotkeyU32.exe"

;open file for read:
oFile := FileOpen(vPath, "r")
if !oFile
{
	MsgBox, % "error: could not open file"
	return
}

;get file encoding:
MsgBox, % oFile.Encoding ;e.g. CP1252/UTF-8/UTF-16

;get file size (bytes):
MsgBox, % oFile.Length ;e.g. 896000, around 1 MB

;get file pointer position:
MsgBox, % oFile.Pos ;0
MsgBox, % oFile.Position ;0
MsgBox, % oFile.Tell() ;0 ;note: Tell is deprecated

;set file pointer position:
oFile.Pos := 10
;oFile.Position := 10 ;equivalent to line above
;oFile.Seek(10) ;equivalent to line above
MsgBox, % oFile.Pos ;10
MsgBox, % oFile.Position ;10
MsgBox, % oFile.Tell() ;10 ;note: Tell is deprecated

;check if file pointer at EOF (end of file):
oFile.Pos := oFile.Length - 1
MsgBox, % oFile.Pos " " oFile.AtEOF ;e.g. 895999 0
oFile.Pos := oFile.Length
MsgBox, % oFile.Pos " " oFile.AtEOF ;e.g. 896000 1
oFile.Pos := oFile.Length + 1
MsgBox, % oFile.Pos " " oFile.AtEOF ;e.g. 896001 1

;use the handle to get the file path:
;note: GetFinalPathNameByHandle doesn't work on Windows XP
vChars := DllCall("kernel32\GetFinalPathNameByHandle", "Int",oFile.__Handle, "Ptr",0, "UInt",0, "UInt",0, "UInt")
VarSetCapacity(vPathLong, (vChars+1)*(A_IsUnicode?2:1), 0)
DllCall("kernel32\GetFinalPathNameByHandle", "Int",oFile.__Handle, "Str",vPathLong, "UInt",vChars, "UInt",0, "UInt")
if (SubStr(vPathLong, 1, 4) = "\\?\")
	vPathLong := SubStr(vPathLong, 5)
MsgBox, % vPathLong

;use the handle to get the file modified/created dates (time zone: UTC):
;note: you can use SetFileTime to set the time
DllCall("kernel32\GetFileTime", "Ptr",oFile.__Handle, "Int64*",vIntervalsC, "Int64*",vIntervalsA, "Int64*",vIntervalsM)
vDateM := vDateC := 1601
EnvAdd, vDateM, % vIntervalsM//10000000, Seconds
EnvAdd, vDateC, % vIntervalsC//10000000, Seconds
FormatTime, vDateM, % vDateM, yyyy-MM-dd HH:mm:ss
FormatTime, vDateC, % vDateC, yyyy-MM-dd HH:mm:ss
MsgBox, % "mod: " vDateM "`r`n" "cre: " vDateC

;close file
oFile.Close()
return
==================================================

FILE OBJECT EXAMPLES: TEST READ/WRITE

File object methods for writing text/data:

Code: Select all

;test write text
vPath := A_Desktop "\z " A_Now ".txt"

;open file:
oFile := FileOpen(vPath, "w`n") ;`n option: stand-alone `n to `r`n when writing
if !oFile
{
	MsgBox, % "error: could not open file"
	return
}

MsgBox, % oFile.Pos ;0

;write 5 characters:
oFile.Write("abc`r`n")
MsgBox, % oFile.Pos ;5

;write a string and write a line break:
oFile.WriteLine("def")
MsgBox, % oFile.Pos ;10

;write individual bytes by specifying numbers:
oFile.WriteUChar(103)
oFile.WriteUChar(104)
oFile.WriteUChar(105)
oFile.WriteUChar(13)
oFile.WriteUChar(10)
MsgBox, % oFile.Pos ;15

;write multiple bytes by copying data:
VarSetCapacity(vData, 3)
StrPut("jkl", &vData, "CP0")
oFile.RawWrite(&vData, 3)
MsgBox, % oFile.Pos ;18

oFile.Close()
return
File object methods for reading text/data:

Code: Select all

;test read text

;get file path:
vFilePattern := A_Desktop "\z *.txt"
vDateM := 0
vPath := ""
Loop Files, % vFilePattern, % "F"
{
	if (A_LoopFileTimeModified > vDateM)
	{
		vDateM := A_LoopFileTimeModified
		vPath := A_LoopFileFullPath
	}
}
if !FileExist(vPath)
{
	MsgBox, % "error: no matching file found"
	return
}

;open file:
oFile := FileOpen(vPath, "r")
;oFile := FileOpen(vPath, "r`n") ;`n option: `r`n to `n when reading
if !oFile
{
	MsgBox, % "error: could not open file"
	return
}

MsgBox, % oFile.Pos ;0

;read 5 characters:
vText := oFile.Read(5)
vText := StrReplace(vText, "`r", "_R")
vText := StrReplace(vText, "`n", "N_")
MsgBox, % "[" vText "]"
MsgBox, % oFile.Pos ;5

;read up till next line break (including line break):
vText := oFile.ReadLine()
;vText := RTrim(vText, "`r`n") ;use this to trim trailing CRs/LFs
vText := StrReplace(vText, "`r", "_R")
vText := StrReplace(vText, "`n", "N_")
MsgBox, % "[" vText "]"
MsgBox, % oFile.Pos ;10

;read individual bytes as numbers:
vText := ""
Loop 5
	vText .= Chr(oFile.ReadUChar())
vText := StrReplace(vText, "`r", "_R")
vText := StrReplace(vText, "`n", "N_")
MsgBox, % "[" vText "]"
MsgBox, % oFile.Pos ;15

;read multiple bytes by copying data:
VarSetCapacity(vData, 3)
oFile.RawRead(&vData, 3)
vText := StrGet(&vData, 3, "CP0")
MsgBox, % "[" vText "]"
MsgBox, % oFile.Pos ;18

;a simple loop to read each line of a text file:
oFile.Pos := 0
while !oFile.AtEOF
	MsgBox, % "[" RTrim(oFile.ReadLine(), "`r`n") "]"

oFile.Close()
return
==================================================

FILE OBJECT EXAMPLES: WARNING: 'OPENING' A FILE CAN EMPTY ITS CONTENTS

A warning re. opening a file in 'w' mode:

Code: Select all

;warning re. 'w' mode: 'opening' a file can empty its contents
vPath := A_Desktop "\z " A_Now ".txt"
FileAppend, % "abcdefghijklmnopqrstuvwxyz", % "*" vPath
FileGetSize, vSize, % vPath
MsgBox, % vSize ;26
FileOpen(vPath, "w")
FileGetSize, vSize, % vPath
MsgBox, % vSize ;0
return
A demonstration that this does not occur with 'rw' mode:

Code: Select all

;opening a file via 'rw' mode, does not empty the file:
vPath := A_Desktop "\z " A_Now ".txt"
FileAppend, % "abcdefghijklmnopqrstuvwxyz", % "*" vPath
FileGetSize, vSize, % vPath
MsgBox, % vSize ;26
FileOpen(vPath, "rw")
FileGetSize, vSize, % vPath
MsgBox, % vSize ;26
return
An example to edit part of a file (using 'rw' mode):

Code: Select all

;edit part of a file (using 'rw' mode)
vPath := A_Desktop "\z " A_Now ".txt"
FileAppend, % "abcdefghijklmnopqrstuvwxyz", % "*" vPath
oFile := FileOpen(vPath, "rw")
if !oFile
{
	MsgBox, % "error: could not open file"
	return
}
oFile.Pos := 12
oFile.WriteUChar(Ord("M"))
oFile.WriteUChar(Ord("N"))
oFile.Close()
FileRead, vText, % vPath
MsgBox, % vText
return
==================================================

FILE OBJECT EXAMPLES: TEST EOL OPTIONS

The `r and `n options, used by FileOpen, can alter CRs/LFs when reading/writing from files:

Code: Select all

;comparing `n/`r modes on/off (reading):
vPath := A_Desktop "\z " A_Now ".txt"
FileAppend, % "_`r`n_`r_`n_", % "*" vPath
MsgBox, % FileOpen(vPath, "r").Length() ;8
;MsgBox, % FileOpen(vPath, "r`n").Length() ;8 ;option doesn't matter
;MsgBox, % FileOpen(vPath, "r`r").Length() ;8 ;option doesn't matter
;MsgBox, % FileOpen(vPath, "r`r`n").Length() ;8 ;option doesn't matter
MsgBox, % StrLen(FileOpen(vPath, "r").Read()) ;8
MsgBox, % StrLen(FileOpen(vPath, "r`n").Read()) ;7 ;`r`n -> `n
MsgBox, % StrLen(FileOpen(vPath, "r`r").Read()) ;8 ;`r -> `n
MsgBox, % StrLen(FileOpen(vPath, "r`r`n").Read()) ;7 ;`r`n -> `n and `r -> `n
return

Code: Select all

;comparing `n/`r modes on/off (writing):
vPathPart := A_Desktop "\z " A_Now " "
Loop 4
	vPath%A_Index% := vPathPart A_Index ".txt"
MsgBox, % vPath1
FileOpen(vPath1, "w").Write("_`r`n_`r_`n_") ;size: 8
FileOpen(vPath2, "w`n").Write("_`r`n_`r_`n_") ;size: 9 ;`n -> `r`n
FileOpen(vPath3, "w`r").Write("_`r`n_`r_`n_") ;size: 8
FileOpen(vPath4, "w`r`n").Write("_`r`n_`r_`n_") ;size: 9 ;`n -> `r`n

Loop 4
{
	vPath := vPath%A_Index%
	FileRead, vText, % vPath
	vText := StrReplace(vText, "`r", "_R")
	vText := StrReplace(vText, "`n", "N_")
	FileGetSize, vSize, % vPath
	MsgBox, % vSize "`r`n" vText
}
return
==================================================

FILE OBJECT EXAMPLES: TEXT ENCODINGS

Create an empty UTF-8 file:

Code: Select all

;create an empty text file with a UTF-8 BOM
vPath := A_Desktop "\z " A_Now ".txt"
oFile := FileOpen(vPath, "w", "UTF-8")
if !oFile
{
	MsgBox, % "error: could not open file"
	return
}
oFile.Close()

;create an empty text file with a UTF-8 BOM (alternative)
vPath := A_Desktop "\z " A_Now ".txt"
oFile := FileOpen(vPath, "w")
if !oFile
{
	MsgBox, % "error: could not open file"
	return
}
oFile.Length := 0 ;empty file
oFile.Encoding := "UTF-8"
oFile.Write(Chr(0xFEFF)) ;BOM
oFile.Close()

;create an empty text file with a UTF-8 BOM (another alternative)
vPath := A_Desktop "\z " A_Now ".txt"
oFile := FileOpen(vPath, "w")
if !oFile
{
	MsgBox, % "error: could not open file"
	return
}
oFile.WriteUInt(0x00BFBBEF) ;bytes: EF BB BF
oFile.Length := 3
oFile.Close()
Create ANSI/UTF-8/UTF-16 files (and UTF-8/UTF-16 files with/without BOMs):

Code: Select all

;create ANSI/UTF-8/UTF-16 files
;note: UTF-16 is labelled 'Unicode' in Notepad

vPathPart := A_Desktop "\z " A_Now " "
FileOpen(vPathPart "CP1252.txt", "w", "CP1252").Write("abc")
FileOpen(vPathPart "UTF-8.txt", "w", "UTF-8").Write("abc")
FileOpen(vPathPart "UTF-16.txt", "w", "UTF-16").Write("abc")
FileOpen(vPathPart "UTF-8-RAW.txt", "w", "UTF-8-RAW").Write("abc")
FileOpen(vPathPart "UTF-16-RAW.txt", "w", "UTF-16-RAW").Write("abc")

;note: if you open a file with a UTF-8/UTF-16 BOM, the file pointer starts after the BOM
MsgBox, % FileOpen(vPathPart "UTF-8.txt", "r").Pos ;3
MsgBox, % FileOpen(vPathPart "UTF-16.txt", "r").Pos ;2

vOutput := ""
vFilePattern := vPathPart "*.txt"
Loop Files, % vFilePattern, % "F"
{
	vPath := A_LoopFileFullPath
	SplitPath, vPath, vName, vDir, vExt, vNameNoExt, vDrive
	vSize := A_LoopFileSize
	vOutput .= vSize "`t" vName "`r`n"
}
MsgBox, % vOutput
Create a UTF-16 BE file:

Code: Select all

;create UTF-16 BE file, with A BOM ('Unicode big endian' in Notepad)
vPath := A_Desktop "\z " A_Now " UTF-16 BE.txt"
vText := Chr(0xFEFF) "abc" ;with BOM
vText := "abc" ;without BOM
vSize := StrLen(vText)*2
VarSetCapacity(vData2, vSize+2)
StrPut(vText, &vData2, "UTF-16")
VarSetCapacity(vData, vSize)
;LCMAP_BYTEREV := 0x800
DllCall("kernel32\LCMapStringW", "UInt",0, "UInt",0x800, "WStr",vData2, "Int",vSize/2, "WStr",vData, "Int",vSize/2)
FileOpen(vPath, "w").RawWrite(&vData, vSize)
==================================================

FILE OBJECT EXAMPLES: IDENTIFY ENCODINGS / FILETYPES

Identify the encoding of a txt file:

Code: Select all

;identify the encoding of a txt file
vPath := A_ScriptFullPath
vEnc := ""
if (oFile := FileOpen(vPath, "r"))
{
	vEnc := oFile.Encoding ;e.g. CP1252/UTF-8/UTF-16
	if (SubStr(vEnc, 1, 3) = "UTF")
	{ ;do nothing: vEnc is UTF-8/UTF-16
	}
	else if (oFile.ReadUShort() = 0xFFFE) ;bytes: 'FE FF'
		vEnc := "UTF-16 BE"
	else
		vEnc := "ANSI"
	oFile.Close()
}
MsgBox, % vEnc "`r`n" vPath
return
Check if a file contains null bytes:

Code: Select all

;a file that contains a null byte is probably binary data,
;although it could be a UTF-16 file,
;ANSI/UTF-8 text files do not contain null bytes,

;q:: ;check if file contains null bytes
vPath1 := A_AhkPath
vPath2 := A_ScriptFullPath
Loop 2
{
	vPath := vPath%A_Index%
	oFile := FileOpen(vPath, "r")
	vSize := oFile.Length
	oFile.Pos := 0
	VarSetCapacity(vData, oFile.Length+1)
	NumPut(0, &vData+oFile.Length, 0, "UChar")
	oFile.RawRead(&vData, oFile.Length)
	oFile.Close()

	;note: a UTF-16 LE file using English language characters,
	;will commonly contain null bytes at odd offsets,
	;e.g. 'A' is Chr(0x61), stored as bytes '61 00'
	;MsgBox, % NumGet(&vData, 0, "UChar") ;255 for a UTF-16 LE BOM
	;MsgBox, % NumGet(&vData, 1, "UChar") ;254 for a UTF-16 LE BOM, 0 for a null byte
	;MsgBox, % NumGet(&vData, 3, "UChar") ;0 for a null byte
	;MsgBox, % NumGet(&vData, 5, "UChar") ;0 for a null byte

	vLenAnsi := StrLen(StrGet(&vData, "CP0"))
	vLenAnsi2 := DllCall("msvcrt\strlen", "Ptr",&vData, "Cdecl UPtr") ;equivalent to line above
	vContainsNulls := !(vLenAnsi = vSize)
	MsgBox, % vContainsNulls " [" vLenAnsi " " vLenAnsi2 " " vSize "]`r`n" vPath
}
return
Create UTF-8 byte sequences (to check if a file may be ANSI/UTF-8 without a BOM):

Code: Select all

;check if an ANSI text file might be UTF-8

;note: a UTF-8 file will contain common strings not in an ANSI file
;€ €
;£ £
;é é

VarSetCapacity(vData, 2, 0)
VarSetCapacity(vDataUtf8, 4, 0)
Loop, 255
{
	if (A_Index <= 127)
		continue
	NumPut(A_Index, &vData, 0, "UChar")
	vUtf16 := StrGet(&vData, "CP1252")
	vOrd := Ord(vUtf16)
	StrPut(vUtf16, &vDataUtf8, "UTF-8")
	vUtf8Bytes := StrGet(&vDataUtf8, "CP1252")
	vOutput .= A_Index "`t" vOrd "`t" vUtf16 "`t" vUtf8Bytes "`r`n"
}
Clipboard := vOutput
MsgBox, % vOutput
return
==================================================

FILE OBJECT EXAMPLES: READ BINARY DATA AND REPLACE NULLS

To read binary data to a variable, and replace null bytes with an ANSI character:
use msvcrt\strlen to find null bytes and NumPut to overwrite them, then obtain a string via StrGet.

Read binary data to a variable, and replace null bytes with an Unicode character:
use MultiByteToWideChar to convert the bytes to shorts, use msvcrt\wcslen to find null shorts and NumPut to overwrite them, then obtain a string via StrGet.

Code: Select all

q:: ;null bytes to an ANSI character
;view a file's binary data as a string
;convert null bytes to spaces (like Notepad) or another ANSI character
vChar := " " ;character to replace null characters
vOrd := Ord(vChar)

vPath := A_AhkPath
;Run, % "notepad.exe " Chr(34) vPath Chr(34)
oFile := FileOpen(vPath, "r")
vSize := oFile.Length
oFile.Pos := 0

VarSetCapacity(vData, oFile.Length+1)
;note: we ensure 1 null byte after the data,
;so that msvcrt\strlen,
;does not search indefinitely for null bytes
NumPut(0, &vData+oFile.Length, 0, "UChar")

oFile.RawRead(&vData, oFile.Length)
oFile.Close()

vAddr := &vData
vEnd := &vData + vSize
Loop
{
	vLen := DllCall("msvcrt\strlen", "Ptr",vAddr, "Cdecl UPtr")
	vAddr += vLen
	if (vAddr >= vEnd)
		break
	NumPut(vOrd, vAddr+0, 0, "UChar")
	vAddr++
}

vCP := 0 ;CP_ACP := 0 (local)
;vCP := 1252 ;Windows-1252 ANSI
;vCP := 28591 ;ISO 8859-1 (equivalent to Unicode 0-255)
Clipboard := StrGet(&vData, "CP" vCP)
MsgBox, % "done"
return

;==================================================

w:: ;null bytes to a Unicode character
;view a file's binary data as a string
;convert null bytes to a Unicode character e.g. square root sign
vChar := Chr(0x221A) ;Chr(8730) ;character to replace null characters
vOrd := Ord(vChar)

vPath := A_AhkPath
;Run, % "notepad.exe " Chr(34) vPath Chr(34)
oFile := FileOpen(vPath, "r")
vSize := oFile.Length
oFile.Pos := 0
VarSetCapacity(vDataTemp, oFile.Length)
oFile.RawRead(&vDataTemp, oFile.Length)
oFile.Close()

VarSetCapacity(vData, vSize*2+2)
;note: we ensure 2 null bytes after the data,
;so that msvcrt\wcslen,
;does not search indefinitely for null shorts
NumPut(0, &vData+vSize*2, 0, "UShort")

;bytes to shorts
;convert bytes to shorts (by converting ANSI to Unicode) - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=5&t=57900
vCP := 0 ;CP_ACP := 0 (local)
;vCP := 1252 ;Windows-1252 ANSI
;vCP := 28591 ;ISO 8859-1 (equivalent to Unicode 0-255)
vRet := DllCall("kernel32\MultiByteToWideChar", "UInt",vCP, "UInt",0, "Ptr",&vDataTemp, "Int",vSize, "Ptr",&vData, "Int",vSize)

vAddr := &vData
vEnd := &vData + vSize*2
Loop
{
	vLen := DllCall("msvcrt\wcslen", "Ptr",vAddr, "Cdecl UPtr")
	vAddr += vLen*2
	if (vAddr >= vEnd)
		break
	NumPut(vOrd, vAddr+0, 0, "UShort")
	vAddr += 2
}
Clipboard := StrGet(&vData, "UTF-16")
MsgBox, % "done"
return

;==================================================

;in general:

;note: we ensure 3 null bytes after the data,
;so that msvcrt\strlen and msvcrt\wcslen,
;do not search indefinitely for null bytes/shorts
DllCall("kernel32\RtlFillMemory", "Ptr",&vData+oFile.Length, "UPtr",3, "UChar",0)
;NumPut(0, &vData+oFile.Length, 0, "UShort")
;NumPut(0, &vData+oFile.Length+2, 0, "UChar") ;two lines equivalent to RtlFillMemory line

;==================================================
==================================================

LINE MANIPULATION

The AHK File object is not particularly useful for deleting/modifying/inserting a line.

An AHK File object can be used to append a line to the end of a file, although this can be done more easily via FileAppend.

Provided below is code for line manipulation, using InStr/SubStr or AHK arrays.

Using InStr/SubStr for deleting/modifying/inserting a line.
More complex, but potentially faster.

Code: Select all

vPath := A_Desktop "\z " A_Now ".txt"
vText := "abc,def,ghi,jkl,mno"
vText := StrReplace(vText, ",", "`r`n") "`r`n"
FileAppend, % vText, % "*" vPath, UTF-8

;delete 3rd line:
;note: vLine must be 2 or greater
;text must be CRLF-delimited
FileRead, vText, % vPath
vLine := 3
vPos1 := InStr(vText, "`r`n",, 1, vLine-1)
if vPos1
	vPos2 := InStr(vText "`r`n", "`r`n",, vPos1+1)
else
	vPos2 := 0
if vPos1 && vPos2
{
	vText := SubStr(vText, 1, vPos1+1) SubStr(vText, vPos2+2)
	;vText := StrReplace(vText, "`r", "_R")
	;vText := StrReplace(vText, "`n", "N_")
	MsgBox, % vText
}
else
	MsgBox, % "error: text left unchanged"

;replace 3rd line:
;note: vLine must be 2 or greater
;text must be CRLF-delimited
FileRead, vText, % vPath
vLine := 3
vLineText := "NEW TEXT"
vPos1 := InStr(vText, "`r`n",, 1, vLine-1)
if vPos1
	vPos2 := InStr(vText "`r`n", "`r`n",, vPos1+1)
else
	vPos2 := 0
if vPos1 && vPos2
{
	vText := SubStr(vText, 1, vPos1+1) vLineText SubStr(vText, vPos2)
	;vText := StrReplace(vText, "`r", "_R")
	;vText := StrReplace(vText, "`n", "N_")
	MsgBox, % vText
}
else
	MsgBox, % "error: text left unchanged"

;insert new 3rd line (add a line in-between current 2nd/3rd lines):
;note: vLine must be 2 or greater
;text must be CRLF-delimited
FileRead, vText, % vPath
vLine := 3
vLineText := "NEW TEXT"
vPos1 := InStr(vText, "`r`n",, 1, vLine-1)
if vPos1
{
	vText := SubStr(vText, 1, vPos1+1) vLineText SubStr(vText, vPos1)
	;vText := StrReplace(vText, "`r", "_R")
	;vText := StrReplace(vText, "`n", "N_")
	MsgBox, % vText
}
else
	MsgBox, % "error: text left unchanged"
Using AHK arrays for deleting/modifying/inserting a line.
Simpler, and usually reasonably fast.

Code: Select all

vPath := A_Desktop "\z " A_Now ".txt"
vText := "abc,def,ghi,jkl,mno"
vText := StrReplace(vText, ",", "`r`n") "`r`n"
FileAppend, % vText, % "*" vPath, UTF-8

;delete 3rd line:
;text must be CRLF-delimited or LF-delimited
FileRead, vText, % vPath
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2)
oArray := StrSplit(vText, "`n", "`r")
oArray.Delete(3)
for _, vValue in oArray
	vOutput .= vValue "`r`n"
vOutput := SubStr(vOutput, 1, -2)
MsgBox, % vOutput

;replace 3rd line:
;text must be CRLF-delimited or LF-delimited
FileRead, vText, % vPath
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2)
oArray := StrSplit(vText, "`n", "`r")
oArray.3 := "NEW TEXT"
for _, vValue in oArray
	vOutput .= vValue "`r`n"
vOutput := SubStr(vOutput, 1, -2)
MsgBox, % vOutput

;insert new 3rd line (add a line in-between current 2nd/3rd lines):
;text must be CRLF-delimited or LF-delimited
FileRead, vText, % vPath
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2)
oArray := StrSplit(vText, "`n", "`r")
oArray.InsertAt(3, "NEW TEXT")
for _, vValue in oArray
	vOutput .= vValue "`r`n"
vOutput := SubStr(vOutput, 1, -2)
MsgBox, % vOutput
==================================================

INI MANIPULATION

Note: to use the IniXXX commands, you don't need to use .ini files, you can choose whatever extension you like e.g. .txt.

Ini manipulation examples:

Code: Select all

vPath := A_Desktop "\z " A_Now ".txt"
IniWrite, % "Value1", % vPath, % "Section1", % "Key1"
IniWrite, % "Value2", % vPath, % "Section1", % "Key2"
IniWrite, % "Value3", % vPath, % "Section1", % "Key3"
IniWrite, % "Value1", % vPath, % "Section2", % "Key1"
IniWrite, % "Value2", % vPath, % "Section2", % "Key2"
IniWrite, % "Value3", % vPath, % "Section2", % "Key3"
IniRead, vValue, % vPath, % "Section1", % "Key1"
IniRead, vSection, % vPath, % "Section1"
IniRead, vSectionNames, % vPath
FileRead, vText, % vPath
MsgBox, % vValue
MsgBox, % vSection
MsgBox, % vSectionNames
MsgBox, % vText
Note: the AHK IniXXX commands support ANSI and UTF-16 files.
Here is a workaround to use UTF-8 ini files:
UTF-8 ini files - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=38511

==================================================

FILE OBJECT LINKS (TEXT)

file set text/empty/get encoding/force read with specific encoding - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=47084&p=211817#p211817
FileEncoding behavior - ANSI (CP1252), Unicode (UTF-16) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=45118&p=211815#p211815

[remove first character from a text file]
Help with %A_BACKSPACE% - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=46723&p=210560#p210560

Why you shouldn't use FileAppend to continuously write to the disk - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=61853&p=279089#p279089

[read line alternatives e.g. Loop Read/oFile.ReadLine()]
jeeswg's benchmark tests - Page 3 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=37876&p=197936#p197936

[file encodings/file types]
FileEncoding behavior - ANSI (CP1252), Unicode (UTF-16) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=45118&p=204775#p204775
[see: BITWISE: IDENTIFY A FILE TYPE]
jeeswg's mathematics tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=64545

FileAppend to stdout/stderr - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=44528&p=206736#p206736

==================================================

FILE OBJECT LINKS (BINARY DATA)

[read binary data from a file to a variable]
[compare binary files]
conversion logic, v1 = -> v1 := -> v2, two-way compatibility - Page 5 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=27069&p=169780#p169780
Compare binaryfiles with AHK - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=41039&p=187262#p187262
RegExMatch on binary data from FileRead - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=32393&p=170002#p170002

[read/write binary data to/from files]
How to get the binary or hex code of any file? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=37340&p=171943#p171943
[see JEE_FileReadBin and JEE_FileWriteBin]
commands as functions (AHK v2 functions for AHK v1) - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=29689&p=197355#p197355

[hex to file]
graphics: create icons from scratch - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=35758&p=164466#p164466

StrPut/StrGet + hex/base64 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=50528

[null bytes to spaces]
FileRead & binary data - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=49403&p=219575#p219575
C++: C++ to machine code via TDM-GCC - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=75&t=49554&p=220074#p220074

list all files in a folder and get CRC-32 hashes - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=49114

clipboard: remove individual clipboard formats + save to clp file - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=39522
[clipboard]
conversion logic, v1 = -> v1 := -> v2, two-way compatibility - Page 6 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=27069&p=191068#p191068
[clipboard: JEE_ClipboardFromFile]
commands as functions (AHK v2 functions for AHK v1) - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=29689&p=197355#p197355

[concatenate files]
FileCopy like in DOS: copy file1 + file2 mergedfile - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=50717&p=224386#p224386

How to generate a large sparse file natively only with ahk? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=26633&p=125057#p125057

==================================================

FILE OBJECT LINKS (OTHER)

long path support - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=47705&p=213935#p213935

[unresolved]
check if a file is locked, without locking it - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=27607

==================================================
Last edited by jeeswg on 24 Sep 2019, 13:35, edited 3 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WilburBr
Posts: 14
Joined: 22 Jun 2019, 22:17

Re: jeeswg's File object mini-tutorial

02 Aug 2019, 20:25

Very nice. Thanks :bravo:
BNOLI
Posts: 548
Joined: 23 Mar 2020, 03:55

Re: jeeswg's File object mini-tutorial

09 May 2020, 13:50

Amazing (well, as always). THx 4 sharing this :clap: :thumbup: :clap:
Remember to use [code]CODE[/code]-tags for your multi-line scripts. Stay safe, stay inside, and remember washing your hands for 20 sec !
SOTE
Posts: 1056
Joined: 15 Jun 2015, 06:21

Re: jeeswg's File object mini-tutorial

30 Jul 2020, 13:29

Useful. Well done.

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 9 guests