Lua string.find() error - lua

So I'm writing a Lua script and I tested it but I got an error that I don't know how to fix:
.\search.lua:10: malformed pattern (missing ']')
Below is my code. If you know what I did wrong, it would be very helpful if you could tell me.
weird = "--[[".."\n"
function readAll(file)
local c = io.open(file, "rb")
local j = c:read("*all")
c:close()
return(j)
end
function blockActive()
local fc = readAll("functions.lua")
if string.find(fc,weird) ~= nil then
require("blockDeactivated")
return("false")
else
return("true")
end
end
print(blockActive())
Edit: first comment had the answer. I changed
weird = "--[[".."\n" to weird = "%-%-%[%[".."\r" The \n to \r change was because it was actually supposed to be that way in the first place.

This errors because string.find uses Lua Patterns.
Most non-alpha-numeric characters, such as "[", ".", "-" etc. convey special meaning.
string.find(fc,weird), or better, fc:find(weird) is trying to parse these special characters, and erroring.
You can use these patterns to cancel out your other patterns, however.
weird = ("--[["):gsub("%W","%%%0") .. "\r?\n"
This is a little daunting, but it will hopefully make sense.
the ("--[[") is the orignal first part of your weird string, working as expected.
:gsub() is a function that replaces a pattern with another one. Once again, see Patterns.
"%W" is a pattern that matches every string that isn't a letter, a number, or an underscore.
%%%0 replaces everything that matches with itself (%0 is a string that represents everything in this match), following a %, which is escaped.
So this means that [[ will be turned into %[%[, which is how find, and similar patterns 'escape' special characters.
The reason \n is now \r?\n refers back to these patterns. This matches it if it ends with a \n, like it did before. However, if this is running on windows, a newline might look like \r\n. (You can read up on this HERE). A ? following a character, \r in this case, means it can optionally match it. So this matches both --[[\n and --[[\r\n, supporting both windows and linux.
Now, when you run your fc:find(weird), it's running fc:find("%-%-%[%[\r?\n"), which should be exactly what you want.
Hope this has helped!
Finished code if you're a bit lazy
weird = ("--[["):gsub("%W","%%%0") .. "\r?\n" // Escape "--[[", add a newline. Used in our find.
// readAll(file)
// Takes a string as input representing a filename, returns the entire contents as a string.
function readAll(file)
local c = io.open(file, "rb") // Open the file specified by the argument. Read-only, binary (Doesn't autoformat things like \r\n)
local j = c:read("*all") // Dump the contents of the file into a string.
c:close() // Close the file, free up memory.
return j // Return the contents of the string.
end
// blockActive()
// returns whether or not the weird string was matched in 'functions.lua', executes 'blockDeactivated.lua' if it wasn't.
function blockActive()
local fc = readAll("functions.lua") // Dump the contents of 'functions.lua' into a string.
if fc:find(weird) then // If it functions.lua has the block-er.
require("blockDeactivated") // Require (Thus, execute, consider loadfile instead) 'blockDeactived.lua'
return false // Return false.
else
return true // Return true.
end
end
print(blockActive()) // Test? the blockActve code.

Related

How to capture a string between signs in lua?

how can I extract a few words separated by symbols in a string so that nothing is extracted if the symbols change?
for example I wrote this code:
function split(str)
result = {};
for match in string.gmatch(str, "[^%<%|:%,%FS:%>,%s]+" ) do
table.insert(result, match);
end
return result
end
--------------------------Example--------------------------------------------
str = "<busy|MPos:-750.222,900.853,1450.808|FS:2,10>"
my_status={}
status=split(str)
for key, value in pairs(status) do
table.insert(my_status,value)
end
print(my_status[1]) --
print(my_status[2]) --
print(my_status[3]) --
print(my_status[4]) --
print(my_status[5]) --
print(my_status[6]) --
print(my_status[7]) --
output :
busy
MPos
-750.222
900.853
1450.808
2
10
This code works fine, but if the characters and text in the str string change, the extraction is still done, which I do not want to be.
If the string change to
str = "Hello stack overFlow"
Output:
Hello
stack
over
low
nil
nil
nil
In other words, I only want to extract if the string is in this format: "<busy|MPos:-750.222,900.853,1450.808|FS:2,10>"
In lua patterns, you can use captures, which are perfect for things like this. I use something like the following:
--------------------------Example--------------------------------------------
str = "<busy|MPos:-750.222,900.853,1450.808|FS:2,10>"
local status, mpos1, mpos2, mpos3, fs1, fs2 = string.match(str, "%<(%w+)%|MPos:(%--%d+%.%d+),(%--%d+%.%d+),(%--%d+%.%d+)%|FS:(%d+),(%d+)%>")
print(status, mpos1, mpos2, mpos3, fs1, fs2)
I use string.match, not string.gmatch here, because we don't have an arbitrary number of entries (if that is the case, you have to have a different approach). Let's break down the pattern: All captures are surrounded by parantheses () and get returned, so there are as many return values as captures. The individual captures are:
the status flag (or whatever that is): busy is a simple word, so we can use the %w character class (alphanumeric characters, maybe %a, only letters would also do). Then apply the + operator (you already know that one). The + is within the capture
the three numbers for the MPos entry each get (%--%d+%.%d+), which looks weird at first. I use % in front of any non-alphanumeric character, since it turns all magic characters (such as + into normal ones). - is a magic character, so it is required here to match a literal -, but lua allows to put that in front of any non-alphanumerical character, which I do. So the minus is optional, so the capture starts with %-- which is one or zero repetitions (- operator) of a literal - (%-). Then I just match two integers separated by a dot (%d is a digit, %. matches a literal dot). We do this three times, separated by a comma (which I don't escape since I'm sure it is not a magical character).
the last entry (FS) works practically the same as the MPos entry
all entries are separated by |, which I simply match with %|
So putting it together:
start of string: %<
status field: (%w+)
separator: %|
MPos (three numbers): MPos:(%--%d+%.%d+),(%--%d+%.%d+),(%--%d+%.%d+)
separator: %|
FS entry (two integers): FS:(%d+),(%d+)
end of string: %>
With this approach you have the data in local variables with sensible names, which you can then put into a table (for example).
If the match failes (for instance, when you use "Hello stack overFlow"), nil` is returned, which can simply be checked for (you could check any of the local variables, but it is common to check the first one.

send hex variable via TCP socket

Probably it's an easy thing, but I'm a Lua beginner...
I'm creating a very simple QSC QSYS plugin to control a projection server using KVL API. Server API is based on hex strings.
For example this command asks the server to load a the playlist with 9bf5455689ed4c019731c6dd3c071f0e uuid:
Controls["LoadSPL"].EventHandler = function()
sock:Write(
"\x06\x0e\x2b\x34\x02\x05\x01\x0a\x0e\x10\x01\x01\x01\x03\x09\x00\x83\x00\x00\x14\x00\x00\x00\x01\x9b\xf5\x45\x56\x89\xed\x4c\x01\x97\x31\xc6\xdd\x3c\x07\x1f\x0e"
)
end
Now I need to be able to create a string with a variable UUID, according to the text indicated in a textbox (or a list of available UUIDs read from the server) in the user interface.
I will concatenate this string to the fixed part of the command.
How can I correctly make a string like
ad17fc696b49454db17d593db3e553e5 become
\xad\x17\xfc\x69\x6b\x49\x45\x4d\xb1\x7d\x59\x3d\xb3\xe5\x53\xe5?
Try this:
local input = "ad17fc696b49454db17d593db3e553e5"
local output = input:gsub("%w%w", function(s) return string.char(tonumber(s, 16)) end)
Explanation: this takes every pair of characters, interprets them as base 16 numeric string, and then takes the character with that number, and uses that to replace the original characters.
EDIT: To make it clear what's going on, and why the other answers are wrong, backslash escape sequences like \xad are a feature of the Lua source code, in memory it's represented by a byte with value 173, just like A is represented by a byte with value 65. Trying to concatenate a literal backslash character with hexadecimal characters does not create an escape code. So the way to do that is manually with string.char.
#! /usr/bin/env lua
str = 'ad17fc696b49454db17d593db3e553e5'
strx = ''
for i = 1, #str, 2 do -- loop through every-other position in your string
chars = str :sub( i, i+1 ) -- capture every 2 chars
strx = strx ..'\\x' ..chars
end -- append a literal backslash, the letter x, then those 2 chars
target = [[\xad\x17\xfc\x69\x6b\x49\x45\x4d\xb1\x7d\x59\x3d\xb3\xe5\x53\xe5]]
print( x, x == target ) -- print results, and test if it meets expected target
\xad\x17\xfc\x69\x6b\x49\x45\x4d\xb1\x7d\x59\x3d\xb3\xe5\x53\xe5 true
This can be code-golfed into a one-liner
x=''for i=1,#s,2 do x=x..'\\x'..s:sub(i,i+1)end

Break strings into substrings based on delimiters, with empty substrings

I am using LUA to create a table within a table, and am running into an issue. I need to also populate the NIL values that appear, but can not seem to get it right.
String being manipulated:
PatID = '07-26-27~L73F11341687Per^^^SCI^SP~N7N558300000Acc^'
for word in PatID:gmatch("[^\~w]+") do table.insert(PatIDTable,word) end
local _, PatIDCount = string.gsub(PatID,"~","")
PatIDTableB = {}
for i=1, PatIDCount+1 do
PatIDTableB[i] = {}
end
for j=1, #PatIDTable do
for word in PatIDTable[j]:gmatch("[^\^]+") do
table.insert(PatIDTableB[j], word)
end
end
This currently produces this output:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]='SCI'
[3]='SP'
[3]=table
[1]='N7N558300000Acc'
But I need it to produce:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]=''
[3]=''
[4]='SCI'
[5]='SP'
[3]=table
[1]='N7N558300000Acc'
[2]=''
EDIT:
I think I may have done a bad job explaining what it is I am looking for. It is not necessarily that I want the karats to be considered "NIL" or "empty", but rather, that they signify that a new string is to be started.
They are, I guess for lack of a better explanation, position identifiers.
So, for example:
L73F11341687Per^^^SCI^SP
actually translates to:
1. L73F11341687Per
2.
3.
4. SCI
5. SP
If I were to have
L73F11341687Per^12ABC^^SCI^SP
Then the positions are:
1. L73F11341687Per
2. 12ABC
3.
4. SCI
5. SP
And in turn, the table would be:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]='12ABC'
[3]=''
[4]='SCI'
[5]='SP'
[3]=table
[1]='N7N558300000Acc'
[2]=''
Hopefully this sheds a little more light on what I'm trying to do.
Now that we've cleared up what the question is about, here's the issue.
Your gmatch pattern will return all of the matching substrings in the given string. However, your gmatch pattern uses "+". That means "one or more", which therefore cannot match an empty string. If it encounters a ^ character, it just skips it.
But, if you just tried :gmatch("[^\^]*"), which allows empty matches, the problem is that it would effectively turn every ^ character into an empty match. Which is not what you want.
What you want is to eat the ^ at the end of a substring. But, if you try :gmatch("([^\^])\^"), you'll find that it won't return the last string. That's because the last string doesn't end with ^, so it isn't a valid match.
The closest you can get with gmatch is this pattern: "([^\^]*)\^?". This has the downside of putting an empty string at the end. However, you can just remove that easily enough, since one will always be placed there.
local s0 = '07-26-27~L73F11341687Per^^^SCI^SP~N7N558300000Acc^'
local tt = {}
for s1 in (s0..'~'):gmatch'(.-)~' do
local t = {}
for s2 in (s1..'^'):gmatch'(.-)^' do
table.insert(t, s2)
end
table.insert(tt, t)
end

Escaping strings for gsub

I read a file:
local logfile = io.open("log.txt", "r")
data = logfile:read("*a")
print(data)
output:
...
"(\.)\n(\w)", r"\1 \2"
"\n[^\t]", "", x, re.S
...
Yes, logfile looks awful as it's full of various commands
How can I call gsub and remove i.e. "(\.)\n(\w)", r"\1 \2" line from data variable?
Below snippet, does not work:
s='"(\.)\n(\w)", r"\1 \2"'
data=data:gsub(s, '')
I guess some escaping needs to be done. Any easy solution?
Update:
local data = [["(\.)\n(\w)", r"\1 \2"
"\n[^\t]", "", x, re.S]]
local s = [["(\.)\n(\w)", r"\1 \2"]]
local function esc(x)
return (x:gsub('%%', '%%%%')
:gsub('^%^', '%%^')
:gsub('%$$', '%%$')
:gsub('%(', '%%(')
:gsub('%)', '%%)')
:gsub('%.', '%%.')
:gsub('%[', '%%[')
:gsub('%]', '%%]')
:gsub('%*', '%%*')
:gsub('%+', '%%+')
:gsub('%-', '%%-')
:gsub('%?', '%%?'))
end
print(data:gsub(esc(s), ''))
This seems to works fine, only that I need to escape, escape character %, as it wont work if % is in matched string. I tried :gsub('%%', '%%%%') or :gsub('\%', '\%\%') but it doesn't work.
Update 2:
OK, % can be escaped this way if set first in above "table" which I just corrected
:terrible experience:
Update 3:
Escaping of ^ and $
As stated in Lua manual (5.1, 5.2, 5.3)
A caret ^ at the beginning of a pattern anchors the match at the beginning of the subject string. A $ at the end of a pattern anchors the match at the end of the subject string. At other positions, ^ and $ have no special meaning and represent themselves.
So a better idea would be to escape ^ and $ only when they are found (respectively) and the beginning or the end of the string.
Lua 5.1 - 5.2+ incompatibilities
string.gsub now raises an error if the replacement string contains a % followed by a character other than the permitted % or digit.
There is no need to double every % in the replacement string. See lua-users.
According to Programming in Lua:
The character `%´ works as an escape for those magic characters. So, '%.' matches a dot; '%%' matches the character `%´ itself. You can use the escape `%´ not only for the magic characters, but also for all other non-alphanumeric characters. When in doubt, play safe and put an escape.
Doesn't this mean that you can simply put % in front of every non alphanumeric character and be fine. This would also be future proof (in the case that new special characters are introduced). Like this:
function escape_pattern(text)
return text:gsub("([^%w])", "%%%1")
end
It worked for me on Lua 5.3.2 (only rudimentary testing was performed). Not sure if it will work with older versions.
Why not:
local quotepattern = '(['..("%^$().[]*+-?"):gsub("(.)", "%%%1")..'])'
string.quote = function(str)
return str:gsub(quotepattern, "%%%1")
end
to escape and then gsub it away?
try
line = '"(\.)\n(\w)", r"\1 \2"'
rx = '\"%(%\.%)%\n%(%\w%)\", r\"%\1 %\2\"'
print(string.gsub(line, rx, ""))
escape special characters with %, and quotes with \
Try s=[["(\.)\n(\w)", r"\1 \2"]].
Use stringx.replace() from Penlight Lua Libraries instead.
Reference: https://stevedonovan.github.io/Penlight/api/libraries/pl.stringx.html#replace
Implementation (v1.12.0): https://github.com/lunarmodules/Penlight/blob/1.12.0/lua/pl/stringx.lua#L288
Based on their implementation:
function escape(s)
return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1'))
end
function replace(s,old,new,n)
return (gsub(s,escape(old),new:gsub('%%','%%%%'),n))
end

Error in string.gsub with backslash

local a = "te\st"
local b = string.gsub(a,'\','\\\\')
assert(false,b)
What am I doing wrong?
When I do assert, I want that to the screen the string te\st will be printed... but it's not working
I have a JSON file, that I want to decode it into Lua table. I don't need to print out nothing, I did the assert just to test a local problem.
So what I need is to keep all data in the JSON file that has '\'.
Use [[]] instead of "" or '' if you don't want backslash to have special meaning.
Read about literal strings in the manual.
Have you tried escaping it with the % character instead of \
I don't know if this will help, but I was having a HELL of a time making Lua's gsub match my string with special characters in it that I wanted treated literally... it turned out that instead of using \ as an escape character, or doubling the character, that I needed to prefix the special character with % to make it be treated literally.
Your question wasn't too clear so I'm not 100% sure what you mean. Do you mean that you want the assert to fire when b is equal to the string "te\st"? If so you can do a simple:
assert(b ~= "te\st")
Or I suppse...
assert(b ~= a)
You don't need the gsub. But here it is anyways.
local a = "te\\st"
local b = string.gsub(a,'\\','\\')
assert(false,b)

Resources