Reading data of different types from a data file with multiple lines and headers for each line

Hi,

I have the following text file of multiple lines(this is just a small sample of about 100 lines of data that I need to read).

Note following;

  1. Each line of of data has a header, separated by a comma (I have left the header in so I can edit the text file if necessary - easy to find value to edit)
  2. First line of data has two dates (2020-01-1 and 2020-06-1) that are encased in the hash character(this is what VBA does when it writes a date to a text file - I could strip the characters if necessary if that makes more sense to Julia?
1,#2020-01-01#,#2020-06-30#,1,4368,4368
Location, Elevation, Latitude, Longitude, Year, Sky_TRNSYS
"Toowoomba",598,-27.55,151.95,"2010",1
T_Cool_On, T_Heat_On, Hr_On, Hr_Off, RH_Max, RH_Min, RAD_Max, RH_Kp
25,15,18,7,75,40,300,2.5
T_Ideal, Semi_Permanent, External_Shade
20,0,0
Alpha_Leaf_Air, Latent, Sigma, e_Can, e_Sky, Ratio_Glob_NIR, Ratio_Glob_PAR, Ratio_Heat_CO2, Ratio_Heat_Vap, Ratio_mg_ppm
5,2450000,.0000000567,1,1,.5,.5,0,0,.554```

What I want to do, is assign each of the values to their variable name contained in the header and be of the appropriate type (float63, Int, String or Date...). In the past I have successfully used DelimitedFiles to read data files IF there are NO header lines above each line, as follows (works well):

```open("C:\\Users\\peter\\Documents\\Julia_Code\\Learning\\MyFile_line_1.txt","r")

 Crop_Model, Start_Date, End_Date, Start_Hr, End_Hr, Hours_Total = readdlm("C:\\Users\\peter\\Documents\\Julia_Code\\Learning\\MyFile_line_1.txt",',')```

Questions:
1. How to achieve my goal to be able to read in files and use the header details for each line to define the variable name in the Julia program (the data is NOT an array)? If this can't be done I could pass OVER each header line (how to do this ?) and then just assign the numerical values as I have done above - that would suffice. But how to pass over a line? Presumably I need a loop and a test condition ?
2. How to define (tell Julia) that I am reading a date so that I can use this date in the Julia program? If Julia can't handle dates well, I could translate the date into an hour number in my VBA program and eliminate the use of dates all together - not very elegant though.
3. Once the values are read and assigned to the variable names how do I make then visible and usable to other parts of my program? Do I encase this part of the program in a "Module say Input_Data" and then use the "using Module Input_Data" in the areas of my program that need these variables? - this last point is key to me, as the penny has not dropped on how to structure a Julia program and makes variables visible to different parts of my program

I am hoping some kind soul can help with all of these queries.
Thanks Peter

From your post itā€™s hard to say what part of the structure of your file repeats - does it just go on with more lines that have different headers, or is what you show above a block of information which repeats (i.e. the next block would start with 2, Date, Date, Hour, Hour, Hours)?

Given that you are creating the data yourself, it seems to me that the easiest thing to do would be to remove the line breaks from your file and make sure any piece of information belonging to a single observation (say the same ā€œCrop Modelā€?) is on the same line, with the headers being the first line. Then itā€™s just a case of

using CSV, DataFrames

df = CSV.read("my_vba_export.txt", DataFrame)

and you can access the variables with dot syntax, e.g. df.Location, df.T_Cool_On etc.

If this is not an option, you can open the file and use eachline to iterate through (like in this SO answer). However if you can control the format of the file to be read in yourself it seems unnecessary to jump through those hoops.

On your dates question, Julia has excellent Dates support in the Dates standard library, and CSV.jl should automatically detect the dates in your text file if you remove the hash characters. Otherwise you can always do Date(some_string, dateformat"y-m-d") to parse the date afterwards.

Iā€™m not sure I understand the third part of the question - if you do what I say above, df will be available in whatever scope you ran this in, what is and isnā€™t available to ā€œother partsā€ of your program depends on how your program is structured I guess but also seems like an orthogonal question to the question of parsing a text file with custom format.

Thanks for reply. Each line is a unique variable list. No repeating array like structures. I take your point that I could have one very long line of data (I can control how it is written from my VBA program, although I am not sure there is a limit on line length - there must be 400+ variables) and use DataFrames or for that matter use DelimitedFiles, like I showed, thus avoiding the use of df.xxx. Also, I think the DelimitedFiles method shows the variable names explicitly in the input statement, whereas in DataFrames you donā€™t know what you have got (you would need to include a comment file).

Do you concur with this logic? I am a bit old school, so I might be missing something.

I will try the parse date command to deal with the only two dates.

Regarding structure, I need to build my program further and see how best to put it together. I was hoping to have the IO in separate modules away from where all the number crunching takes place. Ultimately, I am building a system of 130 ODEā€™s, where the values read in above are used to form the coefficients (through a fair bit of manipulation) in front of the RHS of the ODE system. This system of equations is solved for every hour of the year, with the previous hours output being the initial conditions for the next hour.

Thanks Peter

So your table is 1-by-400? That indeed suggests that a table might not be the optimal format for your data :slight_smile:

Maybe a dictionary would be better for this (accessed as, say, mydict["Location"])? Or you could make a struct that has a bespoke layout which makes sense for your 400 variables (e.g. grouping some of them together). I would advise against somehow attempting to create 400 variables in your namespace upon reading in the data.

It seems to me that there are actually 3 different questions here now:

  1. How do I get 400 key-valye pairs from Excel into Julia (here I would still say go via CSV.jl, maybe a 400-by-1 table with columns ā€œvariable_nameā€, ā€œvalueā€)

  2. How do I deal with 400 variables and their values in my program? (That depends a lot more on the peculiars of your variables, e.g. is there a natural way to group them somehow like I said above)

  3. Whatā€™s the overall layout of the program? It seems to me like you want to have a function get_inputs("vba_output.csv") which does step (1) and outputs whatever table/dictionary/custom struct you decided on in (2), then you pass whatever that is to your ODE solver.

1 Like

Thanks for thinking about my problem. The variables from excel are written out from a VBA program using the open file and write commands, much as the same way you do in Fortran and do not appear grouped on any excel sheet(s). Each line in the file is an attempt to logically group the variables (there are 64 lines within the file, with each line having a different number of quantities - headers are above each line of data, although I could remove them as they are only there in case I want to directly edit the input file).I was thinking the method below might work. I have just given the example for the first two lines

lines=readlines(f)
Crop_Model, Start_Date, End_Date, Start_Hr, End_Hr, Hours_Total=lines[2]```
Is there a clever way to parse the elements in line[2] to line up with the variable names on the LHS ?
Crop_Model: Integer
Start_Date : Date
End_date : Date
Start_Hour : Integer
End_Hour : Integer
Total_Hours : Integer

Most of the other values in subsequent lines are float64 although there is the odd string variable

Thanks

Well, it does sound like a Dictionary would be appropriate for your usecase, maybe together with a custom function to parse your format like this:

Always read in 2 lines and match header and value into a dict:

function inputtodict(input, delimiter)
       # do something to skip header here
       output = Dict{String,Any}()
       while !eof(input)
           header = split(readline(str), delimiter)
           values = split(readline(str), delimiter)
           @assert length(header) == length(values)
           for (k,v) in zip(header, values)
               output[k] = trytoinfertype(v)
           end
       end
       return output
end

Now you would just need to handle the header and a function trytoinfertype(v) or similar that attempts to do clever type conversion. I havenā€™t checked but I feel like someone has done this before. You could look into how CSV.jl detects column types: https://github.com/JuliaData/CSV.jl/blob/d2197f0778d573a969083d991b44854ddb299653/src/utils.jl#L357
But you probably donā€™t need that much effort if you know which types will show up. Generally something like:

  • try to parse as Int, if that doesnā€™t work
  • try to parse as Float, if that doesnā€™t work
  • ā€¦ whatever other types you have ā€¦
  • if nothing works itā€™s probably a string
1 Like

All, thanks for the responses. I am working through all and trying various thangs, but no there yet. The following code seems that it should work - no errors but answers not correct.


open(Data_Input_File,"r") do f

  for (i,ln) in enumerate(eachline(f))

    

    if i==2

      Elevation, Latitude, Longitude, Year, Sky_TRNSYS = ln

    end

    if i==4

      Crop_Model, Start_Hr, End_Hr, Hours_Total = ln

    end

    if i==6

      T_Cool_On, T_Heat_On, Hr_On, Hr_Off, RH_Max, RH_Min, RAD_Max, RH_Kp = ln

      println(RH_Kp)

    end

  end

end```
 As you can see I am trying to assign the variable names to their values through the a, b, c ..= ln (I am not worried about the the different data types in the file at this stage). Do you think there is a way to make this work ? Furthermore, if I manage to get the "inputtodict" function to work (the one given by FPGro), does this mean I can just use the variable name, T_Cool_On in an expression and the correct value will be used in the calculation? Apologize for dumb questions. I am a very experience Fortran and VBA programmer and really struggling with the structure of the Julia language. I find so many of the examples on-line deal with strings and arrays and there is not much, if anything, I can find about reading in lines of varying lengths with multiple variables. Tomorrow I will try to get the dictionary function to work, assuming I can easily access the values in my calculations (my question above) - wait your response. Thanks Peter

You are just defining a bunch of local variables and not returning anything:

julia> for i āˆˆ 1:10
           if i == 2
               a, b = rand(2)
           elseif i == 4
               c, d = rand(4)
           end
       end

julia> a
ERROR: UndefVarError: a not defined

As Florian suggested above you should really instantiate a Dict (or other convenient struct) to hold your data before you iterate through the file, and then fill that up as you go along.

But again I think youā€™re unnecessarily making it hard on yourself when you could just parse a CSV relying on the great parsing machinery that has been built for that package. I canā€™t see a situation where manually assigning 400+ variables is the best solution.

2 Likes

Ok i take your point. I will try both methods and put up what I find. If I use dictionary then I guess I will need to access the individual variables using the dic[ā€œvariable_nameā€] construct. I was trying to avoid this as I need to change 100ā€™s of lines of code as most of mathematical equations are already written, as I transferred much of the Julia code from a VBA script and retained the same variable names.

Well you can of course unpack the Dict or other struct in your function body, or maybe look at https://github.com/mauro3/Parameters.jl (specifically @unpack)

So the pattern would be

function ingest_data(file)
    ...whatever code you need to parse the file...
    return my_dict (or a custom struct)
end

function solve_odes(parameter_struct)
    var1, var2, var3, ... = @unpack parameter_struct
    ... all the formulas that use var1, var2 ...
end
1 Like

If you really only need this input-functionality in a script or REPL context, then you can just write global in front if your assignments. This will, well, declare the variables as global in your current scope.

If all of these variables belong to a single sample/station, how many of those do you want to read in at the end? If itā€™s more than a handful, youā€™re probably better off putting it all into a CSV table and just using CSV.jl as Nils already said.

However, if you already have a lot of code that uses these exact headers as variables that are currently undefined, then itā€™s probably best to read your file into a dictionary (as shown above) and then just assign each Variable once after reading in.

Example:
Taking your example file from the 1st post

input = IOBuffer("""
       1,#2020-01-01#,#2020-06-30#,1,4368,4368
       Location, Elevation, Latitude, Longitude, Year, Sky_TRNSYS
       "Toowoomba",598,-27.55,151.95,"2010",1
       T_Cool_On, T_Heat_On, Hr_On, Hr_Off, RH_Max, RH_Min, RAD_Max, RH_Kp
       25,15,18,7,75,40,300,2.5
       T_Ideal, Semi_Permanent, External_Shade
       20,0,0
       Alpha_Leaf_Air, Latent, Sigma, e_Can, e_Sky, Ratio_Glob_NIR, Ratio_Glob_PAR, Ratio_Heat_CO2, Ratio_Heat_Vap, Ratio_mg_ppm
       5,2450000,.0000000567,1,1,.5,.5,0,0,.554
       """)

function inputtodict(input, delimiter = ",")
    # skip header
    headerline = readline(input)
    output = Dict{String,String}()
    while !eof(input)
        names = strip.(split(readline(input), delimiter))
        values = strip.(strip.(split(readline(input), delimiter)),'"') # also strip "
        @assert length(names) == length(values)
        for (k,v) in zip(names, values)
            output[k] = v
        end
    end
    return output
end

you can then do:

julia> myvalues = inputtodict(input)
Dict{String,String} with 27 entries:
  "Semi_Permanent" => "0"
  "Alpha_Leaf_Air" => "5"
  "T_Cool_On"      => "25"
  "Ratio_Glob_PAR" => ".5"
  "Ratio_mg_ppm"   => ".554"
  "Latitude"       => "-27.55"
  "Hr_Off"         => "7"
  "e_Can"          => "1"
  ā‹®                => ā‹®

julia> myvalues["Latitude"]
"-27.55"

and then just assign the appropriate values and do the appropriate conversions before the variables are used:

# all the Ints
Elevation, Sky_TRNSYS, [...], Latent, e_Can, e_Sky = [ parse(Int, myvalues[key]) for key in ["Elevation", "Sky_TRNSYS", [...], "Latent", "e_Can", "e_Sky"] ]
# same for Floats, just change "parse(Int, myvalues[key])" for "parse(Float64, myvalues[key])"
Location = myvalues["Location"]
Year = myvalues["Year"]

and then you have them as variables in this scope:

julia> Location
"Toowoomba"

julia> Elevation
598

PS: you need to put the closing ``` onto a separate line. It didnā€™t close the code blocks in your answers above
PPS: sorry for replying to the wrong person

Many thanks for the detailed explanation. I have managed to build my complete dictionary and made the conversions using the method you outlined above - a lot of typing but now complete and the method understood.

A couple of remaining questions:

  1. How do you assign ā€œinputā€ to the file containing the values? In your example you contained the data in IOBuffer, which I also used for demonstration purposes(cut and pasted from file). Going forward I want to be able to assign the values by reading from a file, which I presumably open in the usual way:

Once open, how to do the assignment?
2. I have dropped dates from my file, although going forward I would like to include. Is there a simple way to assign which values are dates(like you have done for the strings above)? VBA always puts # around a date when written to a file. eg. ,#9-12-2020#,

  1. If there was a requirement to use the variables in more than one scope, do we have to repeat the unpacking process outlined above for each scope?

I am almost there thanks to all the help I have received. I will also try the CSV package as well, as I will need to get my head around this for this and other aspects of the project. Thanks to all who have grappled with my problem

Nils, as you can see I have made considerable progress with putting data into, with a couple of outstanding questions still pending with Florian. For completeness, I have had a look at re-ordering my output from VBA to be in ordered pairs, written to a file, ā€œMyFile_Pairs.txtā€, as you suggested. See small sample below:

"Location","Toowoomba"
"Elevation",598
"Latitude",-27.55
"Longitude",151.95
"Year","2010"
"Sky_TRNSYS",1

The following line reads the file into an array:

df=readdlm("C:\\Users\\peter\\Documents\\Julia_Code\\Learning\\MyFile_Pairs.txt",',',header=false);

println(df[4,1])
println(df[4,2])

Now for some questions:

  1. How best to ā€œpackageā€ this array df to be available to other module in my program?
  2. Rather than making a Dictionary is there any other structure, such as struct that might be easier to define? Perhaps and example using my small data file would be useful
  3. If I used struct or just left the df as an array, and then used ā€œusingā€ to include in another scope or module, would I then need a function to ā€œunpackā€ the array or struct so I can use the variable names directly ( in much the same way Florian outlined to unpack a Dictionary)? I guess I could include this function in the module where I read in the data. In this way this function would be made available in the module with the using xxxx statement as well. Is this thinking correct?

I realise I am a bit slow, but I am making progress due to everyoneā€™s efforts. I am determined to get this inputting of data sorted in the next day or so, so I can get on with the more interesting stuff of solving the equations. Many thanks

Okay hereā€™s an attempt at a reasonably close approximation to what I would try:

Start with a csv file which has 400 columns, each of which has the name of the parameter as the header, and 1 row, which has the values for each parameter. At this point your parsing step is as simple as:

julia> using CSV, DataFrames

julia> input_data = Tables.rowtable(CSV.File("out.csv", pool = false))
1-element Vector{NamedTuple{(:location, :elevation, :latitude, :longitude, :year), Tuple{String, Int64, Float64, Float64, Int64}}}:
 (location = "Toowoomba", elevation = 598, latitude = -27.55, longitude = 151.95, year = 2010)

The result is a vector of NamedTuples (with a length of one, as thereā€™s only one row). NamedTuples work well with the macros in Parameters.jl,

Next, define a struct that holds your parameters:

julia> using Parameters

julia> @with_kw struct ModelParameters
           location::String
           elevation::Int64
           latitude::Float64
           longitude::Float64
           year::Int64
       end
ModelParameters

Note that this is still quite cumbersome if the struct has 400 fields (I also donā€™t know what the compiler will think about this, as Iā€™ve never encountered such a large struct, but I guess performance wonā€™t matter when just passing the parameters around). There might be a way to group things which makes intuitve sense, but that will depend on your application - as an example, you might have a field position which is a Vector{Float64} that holds elevation, longitude, latitude instead of three separate fields.

With your struct defined and the data from VBA read into a NamedTuple, you can then create a ModelParameters instance to collect the parameters and pass them into your solver:

julia> parameters = ModelParameters(input_data[1]...)
ModelParameters
  location: String "Toowoomba"
  elevation: Int64 598
  latitude: Float64 -27.55
  longitude: Float64 151.95
  year: Int64 2010

This parameters object can then be passed into the solver (so your solver should be defined as solve_ode(parameters::ModelParameters). Then at the start of your solve_ode function you do:

julia> @unpack location, elevation, latitude, longitude, year = parameters
ModelParameters
  location: String "Toowoomba"
  elevation: Int64 598
  latitude: Float64 -27.55
  longitude: Float64 151.95
  year: Int64 2010

julia> location
"Toowoomba"

and all your variables are defined.

Thanks. I need to work my way through this. Please note that many of the 400 parameters are manipulated and combined before appearing as "parameters " in front of the dependant variables on the RHS of the system of ODEs. Again thanks for your help.

So first of all, I like the approach Nils proposed with CSV, especially because CSV.jl takes care of parsing to the appropriate types.

The IOBuffer was indeed for demonstration purposes, but opening anything else is pretty easy too:
(See also Networking and Streams Ā· The Julia Language)

# using the inputtodict function
mydictwithvalues = open(inputtodict, "filename.extension") # if file is in the same folder, see pwd()

mydictwithvalues = open(inputtodict, "path/to/file.xt") # if the file is anywhere on your machine
# equivalently:
mydictwithvalues = open("filename or path") do input
                   inputtodict(input)
                   end

# for CSV:
using CSV, DataFrames
input_data = Tables.rowtable(CSV.File("full path or filename", pool = false)) # as Nils already said

You can do that easily by stripping the "#"s and parsing the Date, the second argument to date is the format string: day-month-year, change as necessary

using Dates

julia> thedate = Date(strip( "#02-03-2045#" ,'#'), "dd-mm-yyyy") # thedate will be a Date object
2045-03-02

julia> year(thedate)
2045

julia> dayname(thedate)
"Thursday

If you want to automatically recognize if the String you input is a Date (in your format, starting with ā€œ#ā€), itā€™s as simple as:

using Dates
julia> dateorstring(thestring) = startswith(thestring, "#") ? Date(strip( thestring ,'#'), "dd-mm-yyyy") : thestring
dateorstring (generic function with 1 method)

julia> dateorstring("notadate")
"notadate"

julia> typeof(dateorstring("notadate"))
String

julia> dateorstring("#11-12-2013#")
2013-12-11

julia> typeof(dateorstring("#11-12-2013#"))
Date

# and of course violating the assumption that only Dates start with "#" errors out:
julia> dateorstring("#looks like a date but it's not#")
ERROR: ArgumentError: Unable to parse date time. Expected directive DatePart(dd) at char 1
Stacktrace: [...]

(the ā€œif ? then : elseā€ construct is a ternary operator and works just like regular if else end blocks but is shorter)

Thatā€™s up to how you write it. If you ā€œunpackā€ variables in a local scope, then you would have to repeat it in a different scope. If you call your solver as a function, you can unpack all variables in the outer scope and just call the functions with the variables they need (i.e. make the variables arguments of the functions). Using Nils approach with Parameters, you can just pass around the struct to every function or scope you desire and selectively unpack the variables you need there.

A question on the side: how is your code structured? Are you reading in your files in a REPL and the call the solver by hand, or do you have a script of sorts or will input, manipulation and running a solver be integrated into a single function?

Thank you. I will need to work through all this material and see what I get. I am using Vscode to write a script and will hope to run the program in stand alone mode with the solver in my script. I actually have the program running in VBA, but it takes 4.5 hours to solve for every hour of the year, hence the move to Julia. At this time our team wants to define all inputs in Excel and then read the outputs from the Julia script back into Excel for graphing and further analysis. I know it sounds complicatedā€¦ my initial inclination was to do it in my native language Fortran, but going forward this was seen as a retrograde step.

Whatever works for you I guess, but I had a good laugh at the fact that Fortran is seen as a step back from solving something in VBA and Julia is not :smiley: best of luck to you

If you really intend to transfer things back to Excel, then going with CSV.jl is the most natural solution as it works for both ways.

Florian I tried all of your suggestions and very happy to say they work (I am yet to implement the CSV, struct approach, but I will). Thanks for supporting all my questions. I have a couple of general questions that have intrigued me on my short journey with Julia:

  1. Finding good examples of script. For example in the function you wrote you have the key part,
@assert length(header) == length(values)
           for (k,v) in zip(header, values)
               output[k] = trytoinfertype(v)
           end
How did you know to use these lines? None of the basic documentation material on Julia, I could "google", would lead me to these lines. It just intrigues me how you knew to insert these lines. How long have been using Julia. ? In "my day" you studied the textbook , got to know the language syntax and proceeded with those tools to master the language ... along the way you picked up "tricks" and things to make it easier and write more compact code.
2. Is there a recommended on-line course(s) that would lead one to acquire a comprehensive grasp of the language, such as the code you suggested?
3. I have not used Python, but I am guessing Julia and Python are very similar in structure?
4. Comment: As you probably know Fortran and VBA have both sub-routines and functions, which can appear anywhere in the script. Mostly you would structure your program with a "main program" first and all of the sub-routines and functions, in any order, at the bottom of the program. These programs also accommodate easily global variable definitions and "option explicit" or implicit none, which stops typos. I have struggled to make the move to Julia without these features, but I persist and, thanks to all the help on this site, I can see progress..... I am swept along by the belief of many colleagues that Julia solves the two language program (certainly VBA was a poor choice for the problem I am tackling !) . I do like the concept of "Modelling Toolkit" and some of the other high level representation of problems outlined by Nils and others. This remains the appeal and I sincerely hope I am not let down by speed.

Nils I have transposed much of my input data into a single line file with a header and successfully read in the data. Please note that I stuck with the *.txt extension, as this still gives me a NamedTuple representation (see below for a truncated version of the input file).

input_data = Tables.rowtable(CSV.File("MyFile_Pairs.txt", pool = false));

println(input_data)

@with_kw struct ModelParameters

    Location::String
    Elevation::Float64
    Latitude::Float64
    Longitude::Float64
    Year::String
    Sky_TRNSYS::Int64
    Crop_Model:Int64
    Start_Hr::Int64
    End_Hr:: Int64
    Hours_Total:Int64
end

parameters = ModelParameters(input_data[1]...)

NamedTuple{(:Location, Symbol(" Elevation"), Symbol(" Latitude"), Symbol(" Longitude"), Symbol(" Year"), Symbol(" Sky_TRNSYS"), Symbol(" Crop_Model"), Symbol(" Start_Hr"), Symbol(" End_Hr"), Symbol(" Hours_Total ")),Tuple{String,Int64,Float64,Float64,Int64,Int64,Int64,Int64,Int64,Int64}}[(Location = "Toowoomba",  Elevation = 598,  Latitude = -27.55,  Longitude = 151.95,  Year = 2010,  Sky_TRNSYS = 1,  Crop_Model = 1,  Start_Hr = 1,  End_Hr = 4368,  Hours_Total  = 4368)]

Upon running this program I get the error at the bottom of this text.

Questions:

  1. Any ideas what is causing the error? Please note I have defined my structural variables with capitals to align with my data and program variables.

  2. I thought Tuples used (). eg. nt = (a=1, b=2, ā€¦) whereas Arrays used ? I have tried both without effect

  3. In the statement parameters = ModelParameters(input_data[1]ā€¦) do you really just incide the ellipses or does the line need to be completed in some other way?

Many thanks. I will wait your answers before transferring all on my inputs.

ERROR: LoadError: invalid redefinition of constant ModelParameters
Stacktrace:
 [1] top-level scope at C:\Users\peter\.julia\packages\Parameters\CVyBv\src\Parameters.jl:572
 [2] include_string(::Function, ::Module, ::String, ::String) at .\loading.jl:1088
 [3] include_string(::Module, ::String, ::String) at .\loading.jl:1096
 [4] invokelatest(::Any, ::Any, ::Vararg{Any,N} where N; kwargs::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}) at .\essentials.jl:710
 [5] invokelatest(::Any, ::Any, ::Vararg{Any,N} where N) at .\essentials.jl:709
 [6] inlineeval(::Module, ::String, ::Int64, ::Int64, ::String; softscope::Bool) at c:\Users\peter\.vscode\extensions\julialang.language-julia-1.0.10\scripts\packages\VSCodeServer\src\eval.jl:185
 [7] (::VSCodeServer.var"#61#65"{String,Int64,Int64,String,Module,Bool,VSCodeServer.ReplRunCodeRequestParams})() at c:\Users\peter\.vscode\extensions\julialang.language-julia-1.0.10\scripts\packages\VSCodeServer\src\eval.jl:144
 [8] withpath(::VSCodeServer.var"#61#65"{String,Int64,Int64,String,Module,Bool,VSCodeServer.ReplRunCodeRequestParams}, ::String) at c:\Users\peter\.vscode\extensions\julialang.language-julia-1.0.10\scripts\packages\VSCodeServer\src\repl.jl:124
 [9] (::VSCodeServer.var"#60#64"{String,Int64,Int64,String,Module,Bool,Bool,VSCodeServer.ReplRunCodeRequestParams})() at c:\Users\peter\.vscode\extensions\julialang.language-julia-1.0.10\scripts\packages\VSCodeServer\src\eval.jl:142
 [10] hideprompt(::VSCodeServer.var"#60#64"{String,Int64,Int64,String,Module,Bool,Bool,VSCodeServer.ReplRunCodeRequestParams}) at c:\Users\peter\.vscode\extensions\julialang.language-julia-1.0.10\scripts\packages\VSCodeServer\src\repl.jl:36
 [11] (::VSCodeServer.var"#59#63"{String,Int64,Int64,String,Module,Bool,Bool,VSCodeServer.ReplRunCodeRequestParams})() at c:\Users\peter\.vscode\extensions\julialang.language-julia-1.0.10\scripts\packages\VSCodeServer\src\eval.jl:110
 [12] with_logstate(::Function, ::Any) at .\logging.jl:408
 [13] with_logger at .\logging.jl:514 [inlined]
 [14] (::VSCodeServer.var"#58#62"{VSCodeServer.ReplRunCodeRequestParams})() at c:\Users\peter\.vscode\extensions\julialang.language-julia-1.0.10\scripts\packages\VSCodeServer\src\eval.jl:109
 [15] #invokelatest#1 at .\essentials.jl:710 [inlined]
 [16] invokelatest(::Any) at .\essentials.jl:709
 [17] macro expansion at c:\Users\peter\.vscode\extensions\julialang.language-julia-1.0.10\scripts\packages\VSCodeServer\src\eval.jl:27 [inlined]
 [18] (::VSCodeServer.var"#56#57")() at .\task.jl:356
in expression starting at c:\Users\peter\Documents\Julia_Code\Learning\Read_List_Variable.jl:22