Lua string manipulatuion - lua

A string '321#322#323#324#325'.
here number of digits in each number is 3 but it's not limited to 3 it could be any number.
here are 5 numbers in a string but this number could be anything.
task is to get 321,322,323,324,325 and store in a table so that any operation could be performed over them.
I have tried several string functions like c = c:gsub('%W','') to eliminate those non-alphanumeric characters, but nothing helped.
function encrypter()--FUNCTION 14
c=' '
print('Please enter your message!')
local message=io.read()
lengthOfString=string.len(message)--Inbuit function to get length of a string.
newLine()
print('Please enter your key. Remember this key, otherwise your message wont be decrypted')
newLine()
key=io.read()
key=tonumber(key)
largeSpace()
print("Encrypted message is")
for s=1,lengthOfString do
--print(encryptionFormula(string.byte(message,s),key))
--inbuilt function for converting character of a string to it's respective ASCII value. First place gets character or variable having string whereas second place gets position of that character in the given string.
b=encryptionFormula(string.byte(message,s),key)
c=c..tostring(b)..'#'
--print(c)
b=0
end
print(c)
largeSpace()
print("Now share this message along with the key to the receiver. Don't share your key with anyone, if you don't want your message to be read.")
end

What you're looking for is string.gmatch().
local input = "123#546514#13#548#2315"
local numbers = {}
for number in string.gmatch(input, '%d+') do
table.insert(numbers, number)
end
-- Output the numbers
for index, number in ipairs(numbers) do
print(index, number)
-- This prints:
-- 1 123
-- 2 546514
-- 3 13
-- 4 548
-- 5 2315
end
If you don't know how Lua patterns work, you can read about them in the reference manual or you can have a look at Programming in Lua (the first edition is available for free on their website)

Related

My program won't seem to recognize spaces in Lua

Have to make a program for school. Basically you have a string and you need to make it secret by using a code. The secret string is each letter + the secret code in the alphabet. So BALL would be EDOO with the code 3. A space has location zero in the alphabet and if the location + the code is bigger than 26 the new letter is string + code - 26 now whenever I execute my program and add spaces it just "translates" the first part before the space. FYI, the comments next to it are in Dutch and not that important.
local code = 3
local string = string.upper("spaghetti carbonara") --string
local alfabet = " ABCDEFGHIJKLMNOPQRSTUVWXYZ" --alfabet
local lengte = 6 --lengte van string
for getal1 = 1, lengte do -- for want moet herhaald worden/getal
local letter = string.sub(string, getal1, getal1) -- letter uit de string halen
local locatie = string.find(alfabet, letter) -- letter vinden in alfabet
if locatie + code < 26 then
locatie2 = locatie + code -- nieuwe locatie vinden met code
elseif locatie + code > 26 then
locatie2 = locatie + code - 26
end
local letter2 = string.sub(alfabet, locatie2, locatie2) -- letter die overeenkomt met locatie
io.write(letter2)
end
First of all, you shouldn't be using the variable string like you're doing on line 2. That doesn't break your program only by coincidence because all strings have a metamethod with __index pointing to string. You should rename that variable to something else instead (text, input, str, etc.)
With that out of the way, your program seems to work just fine for me. Adding a bunch of spaces to the input adds a bunch of "C"s to the output. Maybe you've just forgotten to change that local lengte = 6 to local lengte = #string? As it currently is, it will only "translate" the first 6 characters in the string.
As a hint on how to improve this: you can replace the whole encryption / decryption part with a single call to string.gsub if you pass it a table as its third argument:
local code = {}
code["a"] = "b"
code["b"] = "a"
print(string.gsub("abc", ".", code))
-- Prints "bac"
-- "a" gets replaced with "b", as per the table
-- same with "b"
-- "c" does not appear as a key in the table, so it's simply ignored
Then all you need to do is build a table that maps every character you want to replace to what you want to replace it with, which is a bit easier to achieve:
local function decrypt_encrypt_dictionaries(alphabet, offset)
local encrypt, decrypt = {}, {}
-- Code that fills the two tables goes here :D
return encrypt, decrypt
end
local ROT13_encrypt, ROT13_decrypt = dectypt_encrypt_dictionaries("abcde"--[[...and so on]], 13)
local some_text = io.read()
-- Encrypt some text:
local encrypted = string.gsub(some_string, ".", ROT13_encrypt)
-- Decrypt it again:
local original = string.gsub(encrypted, ".", ROT13_decrypt)
-- See if it's the same:
print(original)
As I mentioned above, this only shifts the difficulty from the encryption/decryption to building the two Lua tables, which I'm leaving for you to figure out.

What are some expressions for making automatic commas for large numbers?

An example would be: 1000000 to 1,000,000.
If you could make this in Lua that'd be preferred
Lua-users-wiki has an example implementation for your problem.
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
print(comma_value(1000000))
print(comma_value(111))
print(comma_value(3.141592))
Output:
1,000,000
111
3.141592
Alternate version
function comma_value(n) -- credit http://richard.warburton.it
local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
end
local function commas(number)
return tostring(number) -- Make sure the "number" is a string
:reverse() -- Reverse the string
:gsub('%d%d%d', '%0,') -- insert one comma after every 3 numbers
:gsub(',$', '') -- Remove a trailing comma if present
:reverse() -- Reverse the string again
:sub(1) -- a little hack to get rid of the second return value 😜
end
print(commas(1000000)) -- Pass
print(commas(111)) -- Pass
print(commas(3.141592)) -- Fail

Wireshark Lua dissector utf16 string

I am writing a custom Wireshark Lua dissector. One field in the dissector is a UTF16 string. I tried to specify this field with
msg_f = ProtoField.string("mydissector.msg", "msg", base.UNICODE)
local getMsg = buffer(13) -- starting on byte 13
subtree:add_le(m.msg_f, getMsg)
However, this only adds the first character rather than the whole string. It also raises an Expert Info warning undecoded trailing/stray characters.
What is the correct way to parse a UTF16 string?
You haven't specified the range of bytes that comprises the string. This is typically determined by either an explicit length field or by a NULL-terminator. The exact method of determining the range is dependent upon the particular protocol and field in question.
An example of each type:
If there's a length field, say of 1 byte in length that precedes the string, then you can use something like:
local str_len = buffer(13, 1):le_uint()
subtree:add_le(m.msg_len_f, buffer(13))
if str_len > 0 then
subtree:add_le(m.msg_f, buffer(14, str_len))
end
And if the string is NULL-terminated, you can use something like:
local str = buffer(13):stringz()
local str_len = str:len()
subtree:add_le(m.msg_f, buffer(13, str_len + 1))
These are just pseudo-examples, so you'll need to apply whatever method, possibly none of these, to fit your data.
Refer to the Wireshark's Lua API Reference Manual for more details, or to the Wireshark LuaAPI wiki pages.
The solution I came up with is simply:
msg_f = ProtoField.string("mydissector.msg", "msg")
local getMsg = buffer(13) -- starting on byte 13
local msg = getMsg:le_ustring()
subtree:add(msg_f, getMsg, msg)

How to refactor string containing variable names into booleans?

I have an SPSS variable containing lines like:
|2|3|4|5|6|7|8|10|11|12|13|14|15|16|18|20|21|22|23|24|25|26|27|28|29|
Every line starts with pipe, and ends with one. I need to refactor it into boolean variables as the following:
var var1 var2 var3 var4 var5
|2|4|5| 0 1 0 1 1
I have tried to do it with a loop like:
loop # = 1 to 72.
compute var# = SUBSTR(var,2#,1).
end loop.
exe.
My code won't work with 2 or more digits long numbers and also it won't place the values into their respective variables, so I've tried nest the char.substr(var,char.rindex(var,'|') + 1) into another loop with no luck because it still won't allow me to recognize the variable number.
How can I do it?
This looks like a nice job for the DO REPEAT command. However the type conversion is somewhat tricky:
DO REPEAT var#i=var1 TO var72
/i=1 TO 72.
COMPUTE var#i = CHAR.INDEX(var,CONCAT("|",LTRIM(STRING(i,F2.0)),"|"))>0).
END REPEAT.
Explanation: Let's go from the inside to the outside:
STRING(value,F2.0) converts the numeric values into a string of two digits (with a leading white space where the number consist of just one digit), e.g. 2 -> " 2".
LTRIM() removes the leading whitespaces, e.g. " 2" -> "2".
CONCAT() concatenates strings. In the above code it adds the "|" before and after the number, e.g. "2" -> "|2|"
CHAR.INDEX(stringvar,searchstring) returns the position at which the searchstring was found. It returns 0 if the searchstring wasn't found.
CHAR.INDEX(stringvar,searchstring)>0 returns a boolean value indicating if the searchstring was found or not.
It's easier to do the manipulations in Python than native SPSS syntax.
You can use SPSSINC TRANS extension for this purpose.
/* Example data*/.
data list free / TextStr (a99).
begin data.
"|2|3|4|5|6|7|8|10|11|12|13|14|15|16|18|20|21|22|23|24|25|26|27|28|29|"
end data.
/* defining function to achieve task */.
begin program.
def runTask(x):
numbers=map(int,filter(None,[i.strip() for i in x.lstrip('|').split("|")]))
answer=[1 if i in numbers else 0 for i in xrange(1,max(numbers)+1)]
return answer
end program.
/* Run job*/.
spssinc trans result = V1 to V30 type=0 /formula "runTask(TextStr)".
exe.

Read numbers following a keyword into an array in Fortran 90 from a text file

I have many text files of this format
....
<snip>
'FOP' 0.19 1 24 1 25 7 8 /
'FOP' 0.18 1 24 1 25 9 11 /
/
TURX
560231
300244
70029
200250
645257
800191
900333
600334
770291
300335
220287
110262 /
SUBTRACT
'TURX' 'TURY'/
</snip>
......
where the portions I snipped off contain other various data in various formats. The file format is inconsistent (machine generated), the only thing one is assured of is the keyword TURX which may appear more than once. If it appears alone on one line, then the next few lines will contain numbers that I need to fetch into an array. The last number will have a space then a forward slash (/). I can then use this array in other operations afterwards.
How do I "search" or parse a file of unknown format in fortran, and how do I get a loop to fetch the rest of the data, please? I am really new to this and I HAVE to use fortran. Thanks.
Fortran 95 / 2003 have a lot of string and file handling features that make this easier.
For example, this code fragment to process a file of unknown length:
use iso_fortran_env
character (len=100) :: line
integer :: ReadCode
ReadLoop: do
read (75, '(A)', iostat=ReadCode ) line
if ( ReadCode /= 0 ) then
if ( ReadCode == iostat_end ) then
exit ReadLoop
else
write ( *, '( / "Error reading file: ", I0 )' ) ReadCode
stop
end if
end if
! code to process the line ....
end do ReadLoop
Then the "process the line" code can contain several sections depending on a logical variable "Have_TURX". If Have_TRUX is false you are "seeking" ... test whether the line contains "TURX". You could use a plain "==" if TURX is always at the start of the string, or for more generality you could use the intrinsic function "index" to test whether the string "line" contains TURX.
Once the program is in the mode Have_TRUX is true, then you use "internal I/O" to read the numeric value from the string. Since the integers have varying lengths and are left-justified, the easiest way is to use "list-directed I/O": combining these:
read (line, *) integer_variable
Then you could use the intrinsic function "index" again to test whether the string also contains a slash, in which case you change Have_TRUX to false and end reading mode.
If you need to put the numbers into an array, it might be necessary to read the file twice, or to backspace the file, because you will have to allocate the array, and you can't do that until you know the size of the array. Or you could pop the numbers into a linked list, then when you hit the slash allocate the array and fill it from the linked list. Or if there is a known maximum number of values you could use a temporary array, then transfer the numbers to an allocatable output array. This is assuming that you want the output argument of the subroutine be an allocatable array of the correct length, and the it returns one group of numbers per call:
integer, dimension (:), allocatable, intent (out) :: numbers
allocate (numbers (1: HowMany) )
P.S. There is a brief summary of the language features at http://en.wikipedia.org/wiki/Fortran_95_language_features and the gfortran manual has a summary of the intrinsic procedures, from which you can see what built in functions are available for string handling.
I'll give you a nudge in the right direction so that you can finish your project.
Some basics:
Do/While as you'll need some sort of loop
structure to loop through the file
and then over the numbers. There's
no for loop in Fortran, so use this
type.
Read
to read the strings.
To start you need something like this:
program readlines
implicit none
character (len=30) :: rdline
integer,dimension(1000) :: array
! This sets up a character array with 30 positions and an integer array with 1000
!
open(18,file='fileread.txt')
do
read(18,*) rdline
if (trim(rdline).eq.'TURX') exit !loop until the trimmed off portion matches TURX
end do
See this thread for way to turn your strings into integers.
Final edit: Looks like MSB has got most of what I just found out. The iostat argument of the read is the key to it. See this site for a sample program.
Here was my final way around it.
PROGRAM fetchnumbers
implicit none
character (len=50) ::line, numdata
logical ::is_numeric
integer ::I,iost,iost2,counter=0,number
integer, parameter :: long = selected_int_kind(10)
integer, dimension(1000)::numbers !Can the number of numbers be up to 1000?
open(20,file='inputfile.txt') !assuming file is in the same location as program
ReadLoop: do
read(20,*,iostat=iost) line !read data line by line
if (iost .LT. 0) exit !end of file reached before TURX was found
if (len_trim(line)==0) cycle ReadLoop !ignore empty lines
if (index(line, 'TURX').EQ.1) then !prepare to begin capturing
GetNumbers: do
read(20, *,iostat=iost2)numdata !read in the numbers one by one
if (.NOT.is_numeric(numdata)) exit !no more numbers to read
if (iost2 .LT. 0) exit !end of file reached while fetching numbers
read (numdata,*) number !read string value into a number
counter = counter + 1
Storeloop: do I =1,counter
if (I<counter) cycle StoreLoop
numbers(counter)=number !storing data into array
end do StoreLoop
end do GetNumbers
end if
end do ReadLoop
write(*,*) "Numbers are:"
do I=1,counter
write(*,'(I14)') numbers(I)
end do
END PROGRAM fetchnumbers
FUNCTION is_numeric(string)
IMPLICIT NONE
CHARACTER(len=*), INTENT(IN) :: string
LOGICAL :: is_numeric
REAL :: x
INTEGER :: e
is_numeric = .FALSE.
READ(string,*,IOSTAT=e) x
IF (e == 0) is_numeric = .TRUE.
END FUNCTION is_numeric

Resources