ucar.nc2.util.TableParser Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
* See LICENSE for license information.
*/
package ucar.nc2.util;
import ucar.nc2.constants.CDM;
import ucar.unidata.util.StringUtil2;
import java.io.*;
import java.util.*;
import java.net.URL;
/**
* Utility class to read and parse a fixed length table.
* Each line of the table becomes a "Record". Each Record has a set of Fields described by the format string.
*
*
* List recs = TableParser.readTable(is, "3,15,46,54,60d,67d,73d", 50000);
* for (TableParser.Record record : recs) {
* Station s = new Station();
* s.id = "K" + record.get(0);
* s.name = record.get(2) + " " + record.get(3);
* s.lat = (Double) record.get(4) * .01;
* s.lon = (Double) record.get(5) * .01;
* s.elev = (Double) record.get(6);
*
* stationTableHash.put(s.id, s);
* if (showStations) System.out.println(" station= " + s);
* }
*
* Example Table:
* TLX 000001 OKLAHOMA_CITY/Norman OK US 3532 -9727 370 0 NWS
* AMA 000313 AMARILLO/Amarillo TX US 3523 -10170 1093 0 NWS
* HGX 000378 HOUSTON/GALVESTON/Dickinson TX US 2947 -9507 5 0 NWS
* MLB 000302 MELBOURNE/Melbourne FL US 2810 -8065 11 0 NWS
*
* format:
* "3,15,54,60d,67d,73d"
*
* grammer:
* format = {field,}
* field = endPos type
* endPos = ending pos in the line, 0 based, exclusive, ie [start, end)
* type = i=integer, d=double, L=long else String
* field[0] goes from [0, endPos[0])
* field[i] goes from [endPos[i-1] to endPos[i])
*
*
*
* @author caron
*/
/*
ClassLoader cl = Level2VolumeScan.class.getClassLoader();
InputStream is = cl.getResourceAsStream("resources/nj22/tables/nexrad.tbl");
List recs = TableParser.readTable(is, "3,15,46, 54,60d,67d,73d", 50000);
for (TableParser.Record record : recs) {
Station s = new Station();
s.id = "K" + record.get(0);
s.name = record.get(2) + " " + record.get(3);
s.lat = (Double) record.get(4) * .01;
s.lon = (Double) record.get(5) * .01;
s.elev = (Double) record.get(6);
stationTableHash.put(s.id, s);
if (showStations) System.out.println(" station= " + s);
}
1 2 3 4 5 6 7 8 9 10 11 12
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
*/
public class TableParser {
static private final boolean debug = false;
/**
* Reads a URL or file in as a table.
*
* @param urlString starts with http, read URL contenets, else read file.
* @param format describe format of each line.
* @param maxLines maximum number of lines to parse, set to < 0 to read all
* @return List of TableParser.Record
* @throws IOException on read error
* @throws NumberFormatException on parse number error
* @see #readTable(InputStream ios, String format, int maxLines)
*/
static public List readTable(String urlString, String format, int maxLines) throws IOException, NumberFormatException {
InputStream ios;
if (urlString.startsWith("http:")) {
URL url = new URL(urlString);
ios = url.openStream();
} else {
ios = new FileInputStream(urlString);
}
return readTable(ios, format, maxLines);
}
/**
* Reads an input stream, containing lines of ascii in fixed width format.
* Breaks each line into a set of Fields (space or comma delimited) which may be String, integer or double.
*
* @param ios the input stream, will be closed
* @param format describe format of each line.
* @param maxLines maximum number of lines to parse, set to < 0 to read all
* @return List of TableParser.Record
* @throws IOException on read error
* @throws NumberFormatException on parse number error
*/
static public List readTable(InputStream ios, String format, int maxLines) throws IOException, NumberFormatException {
List result;
try {
TableParser parser = new TableParser(format);
result = parser.readAllRecords(ios, maxLines);
} finally {
ios.close();
}
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
private List fields = new ArrayList<>();
public TableParser(String format) throws IOException, NumberFormatException {
int start = 0;
StringTokenizer stoker = new StringTokenizer(format, " ,");
while (stoker.hasMoreTokens()) {
String tok = stoker.nextToken();
// see what type
Class type = String.class;
char last = tok.charAt(tok.length() - 1);
if (last == 'i') type = int.class;
if (last == 'd') type = double.class;
if (last == 'L') type = long.class;
if (type != String.class) tok = tok.substring(0, tok.length() - 1);
int end = Integer.parseInt(tok);
fields.add(new Field(start, end, type));
start = end;
}
}
private String comment = "#";
public void setComment(String comment) {
this.comment = comment;
}
public List readAllRecords(InputStream ios, int maxLines) throws IOException, NumberFormatException {
List records = new ArrayList<>();
BufferedReader dataIS = new BufferedReader(new InputStreamReader(ios, CDM.utf8Charset));
int count = 0;
while ((maxLines < 0) || (count < maxLines)) {
String line = dataIS.readLine();
if (line == null) break;
if (line.startsWith(comment)) continue;
if (line.trim().length() == 0) continue;
if (debug) System.out.printf("%s%n", line);
Record r = Record.make(line, fields);
if (r != null)
records.add(r);
count++;
}
return records;
}
public Field getField(int fldno) {
return fields.get(fldno);
}
public int getNumberOfFields() {
return fields.size();
}
/**
* Describes one field in the record.
*/
public static class Field {
int start, end;
Class type;
boolean hasScale = false;
float scale;
Field(int start, int end, Class type) {
this.start = start;
this.end = end;
this.type = type;
}
protected Field() {}
public Object parse(String line) throws NumberFormatException {
return parse(line, this.start, this.end);
}
public Object parse(String line, int offset) throws NumberFormatException {
return parse(line, this.start+offset, this.end+offset);
}
protected Object parse(String line, int start, int end) throws NumberFormatException {
String svalue;
if (start >= line.length())
svalue = "";
else if (end >= line.length())
svalue = line.substring(start);
else
svalue = line.substring(start, end);
//System.out.printf(" [%d,%d) = %s %n",start, end, svalue);
if (type == String.class)
return svalue;
try {
svalue = StringUtil2.remove(svalue, ' ');
boolean isBlank = (svalue.trim().length() == 0);
if (type == double.class)
return isBlank ? 0.0 : new Double(svalue);
if (type == int.class) {
Integer result = isBlank ? 0 : new Integer(svalue);
if (hasScale)
return result * scale;
else
return result;
}
if (type == long.class)
return isBlank ? 0L : new Long(svalue);
} catch (NumberFormatException e) {
System.out.printf(" [%d,%d) = <%s> %n", start, end, svalue);
throw e;
}
return null;
}
public void setScale(float scale) {
this.scale = scale;
hasScale = true;
}
}
public DerivedField addDerivedField(Field from, Transform transform, Class type ) {
DerivedField fld = new DerivedField(from, transform, type);
fields.add(fld);
return fld;
}
public static class DerivedField extends Field {
Field from;
Transform transform;
DerivedField(Field from, Transform transform, Class type) {
this.from = from;
this.transform = transform;
this.type = type;
}
protected Object parse(String line, int start, int end) throws NumberFormatException {
Object org = from.parse(line);
return transform.derive(org);
}
}
public interface Transform {
Object derive(Object org);
}
/**
* A set of values for one line.
*/
static public class Record {
List