我需要解析一些具有不同类型分隔符(波浪号、空格、逗号、竖线、插入符号)的文本文件。
根据分隔符的不同,元素的顺序也不同,例如:
comma: A, B, C, D, E
caret: B, C, A, E, D
tilde: C, A, B, D, E
分隔符在文件中是相同的,但从一个文件到另一个文件不同。据我所知,数据元素中没有分隔符。
在普通的 Java 中执行此操作的好方法是什么?
我喜欢阅读文件的前两行,然后测试分隔符。如果您在分隔符上拆分,并且两行都返回相同的非零片段数,那么您可能猜对了。这是一个检查文件名.txt 的示例程序。
public static void main(String[] args) throws IOException {
File file = new File("etc/names.txt");
String delim = getDelimiter(file);
System.out.println("Delim is " + delim + " (" + (int) delim.charAt(0) + ")");
}
private static final String[] DELIMS = new String[] { "\t", ",", " " };
private static String getDelimiter(File file) throws IOException {
for (String delim : DELIMS) {
BufferedReader br = new BufferedReader(new FileReader(file));
String[] line0 = br.readLine().split(delim);
String[] line1 = br.readLine().split(delim);
br.close();
if (line0.length == line1.length && line0.length > 1) {
return delim;
}
}
throw new IllegalStateException("Failed to find delimiter for file " + file);
}
我可能会从玩 Java 的StringTokenizer开始。这需要一个字符串,并让您找到由分隔符分隔的每个标记。
但是您想对文件中的内容进行标记。在这种情况下,您可能想要使用 Java 的StreamTokenizer,它可以让您解析来自文件流的输入。
编辑
如果您事先不知道分隔符,您可以做一些事情:
您可以编写一个解析文件的类,如下所示:
interface MyParser {
public MyParser(char delimiter, List<String> fields);
Map<String,String> ParseFile(InputStream file);
}
您将分隔符和有序的字段列表传递给构造函数,然后要求它解析文件。您将获得字段名称(从有序列表)到值的映射。
ParseFile 的实现可能会使用带有分隔符的 split,然后同时遍历 split 返回的数组和字段列表,同时创建映射。
一种可能的方法是使用 Java Compiler Compiler ( https://javacc.dev.java.net/ )。有了这个,您可以为您将接受的内容以及任何时候可能出现的分隔符编写一组规则。根据使用的分隔符,可以为引擎提供解决顺序问题的规则。如有必要,该文件可以沿途切换分隔符。
如果整个文件的分隔符相同,则为一个分隔符编写一个函数,将其命名为d,并在处理其他文件时,将其分隔符替换为d。冲洗。重复。:)
另一种方法:让您的解析函数接受文件名和分隔符作为参数。这假设所有文件的解析逻辑都是相同的。
如果您的文件看起来完全不同 - 分隔符不是您的问题。
如果文件中的分隔符相同,那么很可能在加载文件进行解析时,您可以输入分隔符。
说前..
void someFunction(char delimiter){
--- do wateva you want to do with the file --- // you can use stringTokenizer for this purpose
}
每次加载文件时,您都可以通过将文件的分隔符作为参数调用它来使用此函数。
希望这可以帮助.. :-)
如果在使用特定分隔符时知道记录的确切顺序,我只需创建一个解析器,该解析器将为每一行返回一个 Record 对象......如下所示。
这确实包括很多硬编码的值,但我不确定你需要它有多灵活。我认为这更像是一个脚本/hacky 解决方案,而不是您可以扩展的东西。如果您不知道分隔符,您可以使用 String.split() 方法测试文件的第一行,并查看列数是否与预期计数匹配。
class MyParser
{
public static Record parseLine(String line, char delimiter)
{
StringTokenizer st1 = new StringTokenizer(line, delimiter);
//You could easily use an array instead of these dumb variables
String temp1,temp2,temp3,temp4,temp5;
temp1 = st1.getNextToken();
.. etc..
Record ret = new Record();
switch (delimiter)
{
case '^':
ret.A = temp2;
ret.B = temp3;
...etc...
break;
case '~':
...etc...
break;
}
}
}
class Record
{
String A;
String B;
String C;
String D;
String E:
}
您可以使用前面提到的 StringTokenizer。是的,您需要为所有可能的分隔符指定一个字符串。不要忘记设置标记器的“returnsDelims”属性。这样,您将知道文件中使用了哪个令牌,然后可以相应地解析数据。
在文件中查找分隔符的一种方法是使用某种正则表达式。一个简单的例子是找到任何不是字母或数字的字符:[^A-Za-z0-9]
static String getDelimiter(String str) {
Pattern p = Pattern.compile("([^A-Za-z0-9])");
Matcher m = p.matcher(str.trim()); //remove whitespace as first char(s)
if(m.find())
return m.group(0);
else
return null;
}
public static void main(String[] args) {
String[] str = {" A, B, C, D", "A B C D", "A;B;C;D"};
for(String s : str){
String[] data = s.split(getDelimiter(s));
//do clever stuff with the array
}
}
在这种情况下,我从数组中加载数据,而不是从文件中读取。从文件读取时,将第一行提供给 getDelimiter 方法。
大多数开源 CSV 解析库都允许您更改分隔符,并且还具有内置的行为来处理转义。 Opencsv似乎是现在流行的一种,但我还没有使用它。上次我不得不进行大量 csv 解析时,我对Ostermiller csv 库感到非常满意。