It is currently November 14th, 2019, 7:41 am

Trying to delete specific multiple lines in a file.

Discuss the use of Lua in Script measures.
Dan
Posts: 4
Joined: September 29th, 2014, 11:30 am

Trying to delete specific multiple lines in a file.

Dan » November 24th, 2014, 4:09 pm

Hi,

I'm trying to remove a few specific contiguous lines in a .inc file. I know how the first line starts and how the last line ends, i do not know the line-numbers.
I wanted to use the gsub method to match and replace those lines with ""(nothing), and save the file. Here is how i tried to do this:

Code: Select all

...
Meterfile=io.open("C:\\Users\\SomeUser\\Documents\\Rainmeter\\Skins\\Currentskin\\FileToBeEdited.inc","r");
Meterfile=Meterfile:gsub("%[Result"..MeterToBeRemoved.."Meter%][%s%S]*Y=.*","");
io.close(Meterfile);
...
I get the following error : "attempt to call method 'gsub' (a nil value)"

Is the patternmatching i used incorrect? I'm new to lua and struggling to get through the lua documentation, it would be very kind if someone could point me in the right direction. Thanks
User avatar
iNjUST
Posts: 118
Joined: June 20th, 2012, 12:44 am

Re: Trying to delete specific multiple lines in a file.

iNjUST » November 24th, 2014, 6:46 pm

My initial guess (from a lua newbie): try string.gsub instead.
User avatar
jsmorley
Developer
Posts: 19706
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Trying to delete specific multiple lines in a file.

jsmorley » November 24th, 2014, 10:26 pm

Dan,

You have a bit of a misunderstanding of what a file handle is.

When you open a file with:

fileHandle = io.open('FileName', 'r')

That just "opens" the file for "read", and sets an internal handle, a "pointer" in a sense, of "fileHandle" to that file. That way any file actions you take using that handle take place on that file.

Opening the file with io.open in no way actually "reads" the file, and by no means are the contents of the file ever loaded in the variable fileHandle for you to do gsub on... fileHandle is just an internal numeric "pointer" to the file.

What you will want is a bit more involved than what you have, but not rocket science.

First, here is a text file called Sample.inc that has some Rainmeter "sections" in it:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Variables]

[MeterOne]
Meter=String
FontSize=11
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Sample

[MeterTwo]
Meter=String
FontSize=11
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Sample

[MeterThree]
Meter=String
FontSize=11
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Sample
So let's say we want to remove the entire [MeterTwo] section from it:

In my skin I have the Script measure set up:

Code: Select all

[MeasureScript]
Measure=Script
ScriptFile=#CURRENTPATH#Test.lua
FilePath=#CURRENTPATH#
FileName=Sample.inc
UpdateDivider=-1
Then my Lua:

Code: Select all

function Update()

	filePath = SELF:GetOption('FilePath')
	fileName = SELF:GetOption('FileName')	
	inFile = io.open(filePath .. fileName, 'r')

	fileText = inFile:read('*a')
	inFile:close()
	
	outText = fileText:gsub('%[MeterTwo%].-Text=Sample\n\n', '')
	
	outFile = io.open(filePath .. fileName, 'w+')
	outFile:write(outText)
	outFile:close()
	
	return 'Done'

end
If you are new to Lua, rule ONE is "Everything is always case-sensitive in Lua".

Also, note that I use ' single-quotes instead of " double-quote for strings in Lua. I do this because while both work equally well in Lua, Rainmeter only understands double-quotes, (there is one exception, but it isn't important here) and when sending SKIN:Bang() to Rainmeter from Lua, you will find it both easier to do and to read if you just get in the habit of always using single-quotes in the Lua.

Let's walk through what the Lua is doing:

In the skin's script measure I have an option to set both the FilePath and FileName of the file we want to modify. You can just specify the complete path to the file with one option here, or even just hard code it in the Lua, but do remember that by default the "current directory" when you are in Lua is NOT the current skin folder.

1) I get the FilePath and FileName from the skin script measure using SELF:GetOption()
2) I open the file for read with io.open. I now have a file "handle" called inFile.
3) I read the entire contents of the file into a string variable called fileText.
4) I close that inFile "handle". That closes the file in Windows, and releases the "handle".
5) I create a new string variable called outText that is just fileText without the contents we want removed. (more on that in a second)
6) I open the file again, with the same path and name as before, in "w+" mode. (write, replacing all contents)
7) I write the contents of the variable outText to that file.
8) I close that outFile "handle".

So about that gsub:

outText = fileText:gsub('%[MeterTwo%].-Text=Sample\n\n', '')

What I am doing is looking for %[MeterTwo%] (you need the % chars to "escape" the "[" and "]" chars that are reserved in Lua pattern matching)
^ $ ( ) % . [ ] * + - ? are reserved characters in Lua pattern matching, and need to be escaped with % to use them as a literal. \ is kind a of a special case, as it is reserved in ANY string in Lua, not just pattern matching, and needs to be escaped with \. as in filePath='C:\\Users' to use it as a literal.
Then I am looking for any characters zero or more times, (.- is "ungreedy", which is super important in this scenerio) until it finds the first instance of Text=Sample (the last line in our [MeterTwo] section) and two linefeeds \n\n. I deal with the linesfeeds so we are not left with a hanging linefeed at the end of the line Text=Sample, and the blank line that would have been between the [MeterTwo] and [MeterThree] sections. We don't want two extra blank lines.

I replace all of that with ''. (an empty string, or "nothing")

When I run the skin, the Sample.inc file changes to:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Variables]

[MeterOne]
Meter=String
FontSize=11
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Sample

[MeterThree]
Meter=String
FontSize=11
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Sample
Feel free to ask questions about the Lua. I have to confess I am not a fan of externally editing .ini files with Lua and somehow trying to get "dynamic" skins where you add or remove entire Measure or Meter sections based on some condition, so I'm unlikely to weigh in on any logic issues with that, but I'm glad to help with any Rainmeter or reasonably easy Lua questions. I'm not the world's expert in Lua, but can muddle through usually.
User avatar
jsmorley
Developer
Posts: 19706
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Trying to delete specific multiple lines in a file.

jsmorley » November 25th, 2014, 1:04 am

By the way, a slightly simpler approach, that doesn't require you to "know" the last line of the section you are removing is:

outText = fileText:gsub('%[MeterTwo%].-\n%[', '%[')

What that is doing is looking for [MeterTwo] then skipping until it sees a "[" character at the start of a line. (right after a linefeed) Then it is removing everything except that "[" character. Since the only time a "[" char can be at the beginning of a line in Rainmeter is when it is a section header, that will effectively remove the entire [MeterTwo] section without having to know what is or isn't in [MeterTwo].