1 ;----------------------------------------------------------------------
2 ; This example reads an ASCII file that is formatted a specific way, and
3 ; writes out the results to a netCDF file.
5 ; The first line in the ASCII file must be a header, with each field
6 ; separated by a single character delimiter (like a ","). The rest of
7 ; the file must be such that each row contains all fields, each
8 ; separated by the designated delimiter.
10 ; The fields can be integer, float, double, or string.
11 ;----------------------------------------------------------------------
13 ;----------------------------------------------------------------------
14 ; This function returns the index locations of the given delimiter
15 ; in a row or several rows of strings.
16 ;----------------------------------------------------------------------
17 function delim_indices(strings,nfields,delimiter)
18 local cstrings, cdelim
20 nrows = dimsizes(strings)
22 ; Handle special case if we only have one string. Make sure it
23 ; is put into a 2D array.
26 cstrings = new((/1,strlen(strings)+1/),character)
29 cstrings = stringtochar(strings) ; Convert to characters.
30 cdelim = stringtochar(delimiter) ; Convert delimiter to character.
32 ; Som error checking here. Make sure delimiter is one character.
36 if(rank.ne.1.or.(rank.eq.1.and.nc.ne.2)) then
37 print("delim_indices: fatal: the delimiter you've selected")
38 print("must be a single character. Can't continue.")
43 ; Create array to hold indices of delimiter locations, and then loop
44 ; through each row and find all the delimiters. Make sure each row has
45 ; the correct number of delimiters.
48 cindices = new((/nrows,ndelims/),integer)
50 ii = ind(cstrings(i,:).eq.cdelim(0))
52 ; Make sure there were delimiters on this row. If not, we just quit.
53 ; This could probably be modified to do this more gracefully.
55 if(any(ismissing(ii))) then
56 print("delim_indices: fatal: I didn't find any delimiters")
57 print("('" + delimiter + "') on row " + i + ". Can't continue.")
60 if(dimsizes(ii).ne.ndelims) then
61 print("delim_indices: fatal: I expected to find " + ndelims)
62 print("delimiters on row " + i + ". Instead, I found " + dimsizes(ii) + ".")
63 print("Can't continue.")
69 delete(ii) ; For next time through loop
75 ;----------------------------------------------------------------------
76 ; This function reads in a particular field from a string array,
77 ; given the field number to read (fields start at 1 and go to nfield),
78 ; and the indices of the delimiters.
80 ; It returns either an integer, float, double, or a string,
81 ; depending on the input flag "return_type".
82 ;----------------------------------------------------------------------
83 function read_field(strings,ifield,indices,return_type)
84 local nstring, cstrings, nf, tmp_str
86 nrows = dimsizes(strings)
88 ; Handle special case if we only have one string. Make sure it
89 ; is put into a 2D array.
92 cstrings = new((/1,strlen(strings)+1/),character)
95 cstrings = stringtochar(strings)
96 nf = dimsizes(indices(0,:))+1 ; indices is nrows x (nfields-1)
99 ; Error checking. Make sure user has entered a valid field.
101 if(ifield.le.0.or.ifield.gt.nf) then
102 print("read_field: fatal: you've selected a field that is")
103 print("out-of-range of the number of fields that you have (" + nf + ").")
108 ; Set up array to return.
110 return_array = new(nrows,return_type)
114 ; Special case of first field in row.
118 iend = indices(i,ifield-1)-1
121 ; Special case of first field in row.
123 if(ifield.eq.nf) then
124 ibeg = indices(i,ifield-2)+1
125 iend = dimsizes(cstrings(i,:))-1
127 ; Any field between first and last field.
130 ibeg = indices(i,ifield-2)+1
131 iend = indices(i,ifield-1)-1
135 ; Here's the code that pulls off the correct string, and converts it
136 ; to float if desired.
138 if(return_type.eq."integer") then
139 return_array(i) = stringtointeger(chartostring(cstrings(i,ibeg:iend)))
141 if(return_type.eq."float") then
142 return_array(i) = stringtofloat(chartostring(cstrings(i,ibeg:iend)))
144 if(return_type.eq."double") then
145 return_array(i) = stringtodouble(chartostring(cstrings(i,ibeg:iend)))
147 if(return_type.eq."string") then
148 return_array(i) = chartostring(cstrings(i,ibeg:iend))
155 ;----------------------------------------------------------------------
157 ;----------------------------------------------------------------------
160 ; Set up defaults here. We have to hard-code the field types,
161 ; because we can't really tell otherwise.
163 filename = "data.81" ; ASCII" file to read.
164 cdf_file = filename + ".nc" ; netCDF file to write.
165 nfields = 22 ; # of fields
166 delimiter = "," ; field delimiter
168 ; In this case, fields #6-#8 are strings, fields #2, #3, and #11
169 ; are float, and the rest of the fields are integers.
171 var_types = new(nfields,string)
172 var_types = "integer" ; Most are ints.
173 var_types(5:7) = "string" ; corresponds to fields 6-8
174 var_types(1:2) = "float"
175 var_types(10) = "float"
177 if(isfilepresent(cdf_file))
178 print("Warning: '" + cdf_file + "' exists. Will remove it.")
179 system("/bin/rm " + cdf_file)
183 ; Read in data as strings. This will create a string array that has the
184 ; same number of strings as there are rows in the file. We will then need
185 ; to parse each string later.
187 read_data = asciiread(filename,-1,"string")
188 header = read_data(0) ; Header. Use for variable names.
189 data = read_data(1:) ; Get rid of first line which is a header.
190 nrows = dimsizes(data) ; Number of rows.
193 ; Read in locations of delimiters in each string row.
195 hindices = delim_indices(header,nfields,delimiter) ; header row
196 dindices = delim_indices(data,nfields,delimiter) ; rest of file
199 ; Read in the field names which will become variable names on
202 var_names = new(nfields,string)
205 var_names(i) = read_field(header,i+1,hindices,"string")
208 ; Write out this netCDF file efficiently so it will be faster.
209 ; Try to predefine everything before you write to it.
211 f = addfile(cdf_file,"c")
212 setfileoption(f,"DefineMode",True) ; Enter predefine phase.
215 ; Write global attributes to file. It's okay to do this before
216 ; predefining the file's variables. We are still in "define" mode.
219 fAtt@description = "Data read in from '" + filename + "' ASCII file."
220 fAtt@creation_date = systemfunc ("date")
221 fileattdef( f, fAtt )
224 ; Write dimension name. There's only one here.
226 filedimdef(f,"nvalues",-1,True)
229 ; Define each variable on the file, giving it the dimension name
230 ; we just created above.
232 ; Don't deal with variables that are of type string.
235 if(var_types(i).ne."string") then
236 filevardef(f, var_names(i), var_types(i), "nvalues")
241 ; Loop through each field, read the values for that field, print
242 ; information about the variable, and then write it to the netCDF
246 ifield = i+1 ; Fields start at #1, not #0.
248 ; You can't write strings to a netCDF file, so skip the string fields.
250 if(var_types(i).ne."string") then
251 tmp_data = read_field(data,ifield,dindices,var_types(i))
253 ; Print some info about the variable.
256 print("Writing variable " + var_names(i) + " (field #" + ifield + ").")
257 print("Type is " + var_types(i) + ", min/max = " + min(tmp_data) + \
260 if(any(ismissing(tmp_data))) then
261 print("This variable does contain missing values.")
263 print("This variable doesn't contain missing values.")
266 f->$var_names(i)$ = tmp_data ; Write to netCDF file.
268 delete(tmp_data) ; Delete for next round.