lua table string concat not correct - lua

I have a simple function to read lines from .txt file:
function loadData(file_name, root_path)
-- here, file_name is './list.txt', path is '../data/my/'
for line in io.lines(file_name) do
local data = {}
base_path = root_path .. line
-- so, base_path is something like ../data/my/001
data.file = base_path .. '_color.png'
print(data)
end
end
I expect the data should be {file: "../data/my/001_color.png"}, but I got {_color.png" ../data/my/001}
Can anyone help me? Thanks!

Check your ./list.txt file content for EOL (end of line) as it may be produced on windows (EOL=CR LF) an interpreted on linux (EOL=LF). io.lines takes CR character into line string on linux!
Your programm makes everything correct, but your data is not.
Let assume your first line in ./list.txt is ../data/my/001\r\n
line variable is ../data/my/001\r (print(#line) gives 15 instead of 14 ).
Carriage return (CR) in print moves cursor to start line position witout changing line.
Your print output in this case is something simmilar to {file: "../data/my/001\r_color.png"} (as it depends on print implementation) and you get output:
{file: "../data/my/001
_color.png"} <-- on the same line
Let's combine it:
_color.png"}ata/my/001
To correct this:
provide file without CR (works correctly on all systems)
add in loop on first row: line = line:gsub('[\r\n]','') to remove CR LF

Related

How to load host.conf file variables in lua script

I need to load configuration variables from .conf file in lua script, and use those variables to connect to a database. I have tried using:
require "host.conf"
loadfile("host.conf") - error with unexpected token '#'
os.execute("pathToConfFile/host.lua") - and I have created a lua host file with variables in bash shell
io.popen("host.conf") etc..
None of these solutions are valid.
Is there a way to use the existing host.conf file in lua, and avoid the unexpected token error?
Thank you for your suggestions.
local original = io .open('host.conf')
local hostconf = {} -- copy contents into Lua table
for line in original :lines() do
table .insert( hostconf, line )
end ; io .close( original )
print( hostconf[1] ) -- prints line 1
You haven't specified what format your host.conf comes in, but you'll likely want to parse it better than just throwing contents in a list. Perhaps splitting each line into head / tail, based upon a delimiter ( comma, space, whatever you have between variable & value )
Thank you to everyone who helped out. My question wasn't precise and I had more to learn before I have asked and sorry about that. This is what I used to solve the problem.
local open = io.open
local function read_file(path)
local file = open(path, "r")
if not file then return nil end
local content = file:read "*a" -- *a or *all reads the whole file
local lines = {}
for line in io.lines(path) do
--print(line);
if(line:find(var)~=nil)then
local varStart=string.len(var)+2
local varEnd=string.len(line)
var=string.sub(line,varStart,varEnd)
print(var);
end
--repeat for every line
end
file:close()
return lines;
end
local fileContent = read_file("path");

Print a list that is stored in a variable in LUA

My script uses lfs to read files in a directory.
It then stores the value in a variable called file.
The problem is the value is actually a list.
Here is a sample value.
.
..
a.txt
b.txt
c.txt
d.txt
I can print this variable as is, but I need to integrate this variable inside a dialog box.
When I integrate this variable inside a dialog box, it prints each line in a new dialog.
Here is my code:
require 'lfs'
function main()
for file in lfs.dir[[C:\Users\QXJtaW5pdXM\Desktop\Test\test_3.4.5.6]] do
print(file)
--This works perfectly fine.
Dialog("Title", "Files:\n" .. file)
--This prints each line in a new dialog box.
end
end
return main
I need to print all the files in one dialog box.
If at all possible, I'd love to avoid printing . & ..
As a picture reference, here is what I get:
https://imgur.com/tmfQlan
Here is what I need:
https://imgur.com/mxYBO9t
Could someone please point me in the right direction?
Thank you very very very much!
Your loop's body is executed once for every file so what do you expect if you create a dialog inside the loop? Create it outside after you've created a list of files.
require 'lfs'
function main()
local files = ""
for file in lfs.dir[[C:\Users\QXJtaW5pdXM\Desktop\Test\test_3.4.5.6]] do
files = files .. file .. "\n"
end
Dialog("Title", "Files:\n" .. files)
end
Maybe there is also another function that gives you a list of file names right away.
I had to use table.insert in a for loop to add values inside a table file_list.
Then use table.remove to remove the first two inputs . & ..
In the end, the code would look something like this:
require 'lfs'
file_list = {}
function main()
for grab_files in lfs.dir[[C:\Users\QXJtaW5pdXM\Desktop\Test\test_3.4.5.6]] do
table.insert(file_list, grab_files)
-- table.insert will assign each input of grab_files into each reference of file_list table
end
table.remove(file_list,1)
-- This removes the '.'
table.remove(file_list,1)
-- This removes the '..'
file_names = table.concat(file_list, "\n")
Dialog("Title", "Files:\n" .. file_names)
end
return main

What does file.new("temp.out", "w") line represent?

I'm learning the Ruby language and I'm having a lot of fun.
I am currently working on the Temperature converter with file output exercise.
The solution is provided below
print "Hello. Please enter a Celsius value: "
celsius = gets.to_i
fahrenheit = (celsius * 9 / 5) + 32
puts "saving result to output file 'temp.out'"
fh = File.new("temp.out", "w")
fh.puts fahrenheit
fh.close
The highlighted part confuses me.
We are calling File.new to create a file named "temp.out" and "w" write whatever inputs until we fh.close. Am I correct?
Thank you!
By default, puts() will send its output to what's called stdout, which is connected to your screen. File.new() creates a new file which is assigned to the variable fh. Because you created the file in write mode, you can use fh to write stuff to the file. fh.puts() sends output to the file assigned to the variable fh. In other words, a bare puts() statement sends output to your screen, but when you precede puts() with a file, the output goes to the file.
You can also write those statements like this:
File.open("temp.out", "w") do |f|
f.puts fahrenheit
end
The neat thing about writing it like that is: when the end statement executes, Ruby will automatically close the file for you.

How to detect and convert DOS/Windows end of line to UNIX end of line in Ruby

I have implemented a CSV upload in Ruby (on Rails) that works fine when the file is uploaded from a browser that runs on UNIX-like systems
However I have a file that as uploaded by a real customer contains the famous ^M as end of lines (I guess it was uploaded from Windows)
I need to detect this situation and replace the character before the file is processed
Here is the code that creates the file
# create the file on the server
path = File.join(directory, name)
# write the file
File.open(path, 'wb') { |f| f.write(uploadData.read) }
Do I need to change the "wb" to "w" and this would solve the problem ?
The CR (^M as you say it) char is "\r" in Ruby (and many other languages), so if you're sure your line endings also have the LF char (Windows uses CRLF as the line ending) then you can just remove all the CRs at the ends of the lines ($ matches at the end of a line, before the last "\n"):
uploadData.read.gsub /\r$/, ''
If you're not sure you're going to have the LF (eg. MacOS 9 used to use a plain CR at the end of the line) then replace any CR optionally followed by a LF with an LF:
uploadData.read.gsub /\r\n?/, "\n"

Lua: Pattern match after a string?

For example, I have arbitrary lines in this format:
directory C:\Program Files\abc\def\
or something like.
log-enabled On
I want to be able to extract the "C:\Program Files\ab\def\" part out of that first line. Likewise, I want to extract the "On" out in the second line. The spaces between the variable and its value are arbitrary. I will know the name of the variable, but I need extract the value based on that.
So basically, I want to remove the first word and a number of arbitrary spaces that follow the first word, and return what remains until the end of the line.
Assuming that, by "word" you mean "a string of characters without spaces", you can do this:
for line in ioFile:lines() do
local variable, value = line:match("(%S+)%s+(.+)")
... --Do stuff with variable and value
end
One alternative with string.match was shown by Nicol Bolas, here is another alternative:
function splitOnFirstSpace(input)
local space = input:find(' ') or (#input + 1)
return input:sub(1, space-1), input:sub(space+1)
end
Usage:
local command, param = splitOnFirstSpace(line)
If no argument is given (splitOnFirstSpace('no-param-here')), then param is the empty string.
I do not believe Lua is packaged with a split() function like Ruby or Perl.
I found that this guy built a lua version of Perl's split function:
http://lua-users.org/lists/lua-l/2011-02/msg01145.html
If you can guarantee that the argument will only have 1 word before it, with that word not containing any spaces, you can read in that line, run the split function on it, and use the return array's 1 index value as what you want.
You could error check that too and make sure you get a 'C:\' within your expected directory, or check to make sure the string is == to 'On' or 'Off'. Because of using the hardcoded index value I really advocate you error check your expected value. Nothing is worse than having an assumed value be wrong.
If an error is detected make sure to log or print it to the screen so you know about it.
This could catch bugs where maybe the string that was input is improper.
Some simple code that models what I suggest you do:
line = "directory C:\Program Files\abc\def/";
contents = line.split(" "); --Split using a space
directory = contents[2]; --Here is your directory
if(errorCheckDir(directory))
--Use directory
end
EDIT:
In response to comments below Lua indeed begins indexing at 1, not 0.
Also, in the case that the directory contains spaces (which is probable) instead of simply using contents[2], I would loop through all of contents except index 1, and piece back together the directory making sure to add the required space between each index that you attach.
So in the case above, contents[2] and contents[3] would have to be stitched back together with a space in between to recover the proper directory.
directory = contents[2].." "..contents[3]
This can be easily automated using a function which has a loop in it and returns back the proper directory:
function recoverDir(contents)
directory = "";
--Recover the directory
for i=2, table.getn(contents) do
directory = directory..contents[i].." ";
end
--strip extra space on the end
dirEnd = string.len(directory);
directory = string.sub(directory,1,dirEnd-1);
return directory; --proper directory
end

Resources