I am trying to read a .txt file containing strings:
Delivery LHR 2018
Delivery LHR 2016
Delivery LHR 2014
Delivery LHR 2011
Delivery LHR 2019
Delivery LHR 1998
I have tried below codes but not working. It reported "expect a literal value" when running file-read
globals [input]
to setup
set input []
file-open "test.txt"
while [not file-at-end?]
[
let a quote file-read
let b quote file-read
set input lput a input
set input lput b input
print input
]
file-close
end
to-report quote [ #thing ]
ifelse is-number? #thing
[ report #thing ]
[ report (word "\"" #thing "\"") ]
end
You can kind-of get what you want with the csv extension which comes with NetLogo. It at least let's you specify a delimiter, so " ", but you'll have to manually read past all the blank columns it'll see.
extensions [csv]
globals [input]
to setup
set input []
let lines (csv:from-file "test.txt" " ")
foreach lines [ line ->
let col1 (item 0 line)
let i 1
while [item i line = ""] [ set i (i + 1) ]
let col2 (item i line)
show col2
set i (i + 1)
while [item i line = ""] [ set i (i + 1) ]
let col3 (item i line)
show col3
set input lput col1 input
]
show input
end
The reason it doesn`t work can be found in the file-read description from the NetLogo Dictionary Manual (https://ccl.northwestern.edu/netlogo/docs/dictionary.html#file-read)
[...]Note that strings need to have quotes around them.[...]
It is not a solution to add the quotes within NetLogo because file-read already throws an Error, if the next entry in the file is not one of number, list, string, boolean, or the special value nobody. And string in this case means, it needs to have quotes around it.
Thus, to read the file into NetLogo you have to put quotes around the strings in your input file. Alternatively, if the strings in your input file always have the same length, you could try to read the file using the primitive file-read-characters. Here is an example that should work with your input file:
to setup
file-open "test.txt"
while [not file-at-end?]
[
let a file-read-characters 8
let skip file-read-characters 4
let b file-read-characters 3
let c file-read
print (list a b c)
]
file-close
end
Related
Out of curiosity, I was trying to find a way to combine several inputs into a single input window (currently set to string) on the interface. In the current example, these inputs are the 4 parameters necessary for resize-world.
While my result works, I would be interested to hear if someone else knows a more elegant solution. I specifically dislike the use of item here.
Interface:
input-window
[0 5 0 5]
Code:
to setup
let worldsizes runresult input-window
resize-world item 0 worldsizes item 1 worldsizes item 2 worldsizes item 3 worldsizes
end
Using this post as base, I created a procedure that allows a command, both anonymous and regular, to take a single list of arguments as input.
Interface:
input-window
[0 5 0 5]
Code:
to test
let worldsize run-result input-window ;creates a list
apply-command "resize-world" worldsize
end
to apply-command [command inputlist]
; Takes two inputs
; 1: Anonymous command/string containing anonymous command/string containing regular command
; 2: A list of inputs, corresponding in length to the number of inputs the command takes
; Applies the command with the different items of the list as input
; Heavily inspired by stackoverflow-user Bryan head
set command anonimify-command command length inputlist ; Using non-anonymous commands gave issues, hence they are anonimified
(run listify-command command length inputlist inputlist)
end
to-report listify-command [ command num-args ]
; Takes an anonymous command and the length of a list as input
; Outputs an anonymous command that takes the different items of the list as inputs
; Heavily inspired by stackoverflow-user Bryan head
if (not is-anonymous-command? command) [error "The first input for listify-command has to be an anonymous command"]
let args (reduce word n-values num-args [ x -> (word " item " x " ?") ])
;show (word runresult (word "[ ? -> (run command " args ") ]"))
report runresult (word "[ ? -> (run command " args ") ]")
end
to-report anonimify-command [ command number-of-inputs]
; Takes two inputs
; 1: Takes an anonymous command, a string containing an anonymous command or a string containing a regular command
; 2: Takes a number that corresponds to the number of inputs the anonimified command should take
; Returns anonymous commands unaltered
; Returns strings containing an anonymous command as an anonymous command
; Returns strings containing a regular command as an anonymous command
if (is-anonymous-command? command) [ ; Anonymous commands get returned
report command
]
if (is-string? command) [ ; Strings are processed
carefully [ ; Using run-result on a string that does not contain an anonymous command causes an error, hence carefully
if (is-anonymous-command? run-result command) [
set command run-result command
]
]
[
let inputs n-values number-of-inputs [ i -> word " x" i]
let inputs-as-string reduce word inputs
let command-string (word "[ [" inputs-as-string " ] -> " command inputs-as-string" ]")
set command run-result command-string
]
report command
]
error "The inputted command must be either an anonymous command, a string containing an anonymous command or a string containing a command"
;If the input is neither anonymous command nor string, an error is displayed
end
I've been trying since yesterday to code the following:
to output
file-open ( word NameOutfile-output ".csv" )
ifelse Integer? = true ; interger switch in GUI
[file-print ( word "id;my_xcor;my_ycor" ) file-print ( word self " ; " round my-xcor " ; " round my-yco )]
[file-print ( word "id;my_xcor;my_ycor" ) file-print ( word self " ; " my-xcor " ; " my-ycor )]
file-close
end
I have non-integer coordinate values. I would like to put a switch on the interface if it is turned on it will generate an output with integer values. But, it is giving the following error: ROUND expected input to be a number but got the list [1 2 1 1 1] instead.
I don't know how to close a created .csv file. For example: I export a .csv table. If I run it again, the model will save the results in the table already created. I wish that didn't happen. It is possible?
As you said, NetLogo tells you that round wants a number as input but you're giving it a list.
You can fix this by using map, which lets you run reporters on all elements of a list (and round is a reporter indeed):
to round-my-list
let my-list [3.1 5.6 8.32]
let my-list-rounded (map round my-list)
print my-list-rounded
end
What you are experiencing with the file is the standard behaviour. From the NetLogo Dictionary at file-open, you can read this:
When opening a file in writing mode, all new data will be appended to the end of the original file. [...] If you don't want to append, but want to replace the file's existing contents, use file-delete to delete it first, perhaps inside a carefully if you're not sure whether it already exists.
So if you want every time a new file, you should add something to your code that first tells to delete that file; then, write it again. Using file-delete and carefully in combination as suggested in the NetLogo Dictionary:
to output
carefully
[file-delete ( word NameOutfile-output ".csv" )]
[]
file-open ( word NameOutfile-output ".csv" )
ifelse Integer? = true ; interger switch in GUI
[file-print ( word "id;my_xcor;my_ycor" ) file-print ( word self " ; " round my-xcor " ; " round my-yco )]
[file-print ( word "id;my_xcor;my_ycor" ) file-print ( word self " ; " my-xcor " ; " my-ycor )]
file-close
end
I try to use file-delete word("links" sim ".txt") as I use file-open word("links" sim ".txt"), but an expected parenthesis is asked after sim. Do you know why? How can I manage that?
If you have more than two strings to combine then you need to use the format (word value1 ...) with the parenthesis BEFORE word. See the NetLogo Dictionary for more details. Here is an example that constructs the string you want:
to teststrings
let sim 1
let str1 (word "links" sim ".txt")
print str1
let str2 word "link" sim
print str2
end
I don't understand why your file-open works, are you sure that is the format you are using? The code should generate the same error.
I have some code that reads a file of names and creates a list:
let who-file-name "world-health-field-surveillance.csv"
let who-file-name-dict csv:from-file who-file-name
let who-file-names sort [who] of names
let index 1 ;not 0, this removes the header in the csv
repeat length who-file-names [
file-open "world-health-field-surveillance.csv"
if file-at-end? [stop]
let entry (item 0 (item index who-file-name-dict))
if entry = "\n" [stop]
The file might end with some blank lines or its possible the file has names separated by a newline, like so:
Allman
Atkinson
Behlendorf
I want to ignore any lines that contain only whitespace.
My sample code doesn't work.
How could I do this in netlogo?
What are you trying to do, exactly? If I have a csv file that looks like this:
If I run this code:
extensions [ csv ]
to setup
ca
let example csv:from-file "example_names.csv"
print example
reset-ticks
end
I get a list output that looks like:
[[Allman] [Atkinson] [Behlendorf] [Belnich] [Cravit] [Court]]
Is that not what you're after? If you need just a single-level list, you can do
print reduce sentence example
to get
[Allman Atkinson Behlendorf Belnich Cravit Court]
I am using script below to import from .csv file. Problem is that I want to replace values that in the file are strings or empty (for example #NULL! and empty cell). If I use condition like:
if value = "#NULL!" or value = "" [print "Replace value"]
Nothing happens. I would imagine it is the problem of the encoding or is there some other "feature" involved with netlogo. I also tried to save csv with notepad to get rid of Excel "additons".
CSV import code
to openFile
file-open "/import/import.csv"
set fileList []
while [not file-at-end?] [
set csv file-read-line
set csv word csv "," ; add comma for loop termination
let mylist [] ; list of values
while [not empty? csv] [
let $x position "," csv
let $item substring csv 0 $x ; extract item
carefully [set $item read-from-string $item][] ; convert if number
set mylist lput $item mylist ; append to list
set csv substring csv ($x + 1) length csv ; remove item and comma
]
set fileList lput mylist fileList
]
;show fileList
file-close
end