我有一个看起来像这样的文本文件:
grn129 agri-
ac-214 ahss
hud114 ahss
lov1150 ahss
lov1160 ahss
lov1170 ahss
lov1210 ahss
如果我想创建一个以第一列为键,第二列为值的HashMap,使用Java解析此文件的最佳方法是什么。
我应该使用Scanner类吗?尝试将整个文件作为字符串读入并拆分吗?
最好的方法是什么?
答案 0 :(得分:4)
我会怎么做!自2000年以来,我几乎都是Java程序员,所以它可能有点过时了。有一条线特别值得我自豪:
new InputStreamReader(fin, "UTF-8");
http://www.joelonsoftware.com/articles/Unicode.html
享受!
import java.io.*;
import java.util.*;
public class StackOverflow2565230 {
public static void main(String[] args) throws Exception {
Map<String, String> m = new LinkedHashMap<String, String>();
FileInputStream fin = null;
InputStreamReader isr = null;
BufferedReader br = null;
try {
fin = new FileInputStream(args[0]);
isr = new InputStreamReader(fin, "UTF-8");
br = new BufferedReader(isr);
String line = br.readLine();
while (line != null) {
// Regex to scan for 1 or more whitespace characters
String[] toks = line.split("\\s+");
m.put(toks[0], toks[1]);
line = br.readLine();
}
} finally {
if (br != null) { br.close(); }
if (isr != null) { isr.close(); }
if (fin != null) { fin.close(); }
}
System.out.println(m);
}
}
这是输出:
julius@flower:~$ javac StackOverflow2565230.java
julius@flower:~$ java -cp . StackOverflow2565230 file.txt
{grn129=agri-, ac-214=ahss, hud114=ahss, lov1150=ahss, lov1160=ahss, lov1170=ahss, lov1210=ahss}
是的,我的电脑名称是Flower。以Bambi的臭鼬命名。
最后一点说明:因为close()可以抛出IOException,这就是我真正关闭流的方式:
} finally {
try {
if (br != null) br.close();
} finally {
try {
if (isr != null) isr.close();
} finally {
if (fin != null) fin.close();
}
}
}
答案 1 :(得分:3)
基于@Julius Davies,这是一个较短的版本。
import java.io.*;
import java.util.*;
public class StackOverflow2565230b {
public static void main(String... args) throws IOException {
Map<String, String> m = new LinkedHashMap<String, String>();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(args[0]));
String line;
while ((line = br.readLine()) != null) {
// Regex to scan for 1 or more whitespace characters
String[] toks = line.split("\\s+");
m.put(toks[0], toks[1]);
}
} finally {
if (br != null) br.close(); // dont throw an NPE because the file wasn't found.
}
System.out.println(m);
}
}
答案 2 :(得分:2)
我不知道最好的方法,但我怀疑最有效的方法是一次读取一行(使用BufferedReader),然后通过查找第一个空白字符来拆分每一行,分裂,然后修剪两边。但是,除非你需要超快,否则无论你最喜欢什么都没关系。
我个人偏向于一次性加载整个文件...除了假设有足够的内存来容纳整个文件这一事实外,它不允许任何并行计算(例如,如果输入从管道进来)。能够在仍然生成输入时处理输入是有意义的。
答案 3 :(得分:1)
使用扫描仪或普通的FileReader + String.split()都应该可以正常工作。我认为速度差异很小,除非你打算一遍又一遍地阅读一个非常大的文件,否则没关系。
编辑:实际上,对于第二种方法,请使用BufferedReader。它有一个getLine()方法,使事情稍微容易一些。
答案 4 :(得分:0)
如果您希望遵循教科书解决方案,请使用StringTokenizer。它直截了当,易于学习且非常简单。它可以克服结构中的简单偏差(可变数量的空白字符,不均匀的格式化线等)
但是如果你的文本已经被100%格式化并且可预测,那么只需将一堆行读入缓冲区,一次取出一行,然后将部分字符串取出到HashMap键中,值。它比StringTokenizer更快,但缺乏灵活性。
答案 5 :(得分:0)
如何缓存正则表达式? (String.split()将在每次调用时编译正则表达式)
如果您在几个大文件(100,1k,100k,1m,10m条目)上对每种方法进行性能测试,并且看看性能如何比较,我会很好奇。
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class So2565230 {
private static final Pattern rgx = Pattern.compile("^([^ ]+)[ ]+(.*)$");
private static InputStream getTestData(String charEncoding) throws UnsupportedEncodingException {
String nl = System.getProperty("line.separator");
StringBuilder data = new StringBuilder();
data.append(" bad data " + nl);
data.append("grn129 agri-" + nl);
data.append("grn129 agri-" + nl);
data.append("ac-214 ahss" + nl);
data.append("hud114 ahss" + nl);
data.append("lov1150 ahss" + nl);
data.append("lov1160 ahss" + nl);
data.append("lov1170 ahss" + nl);
data.append("lov1210 ahss" + nl);
byte[] dataBytes = data.toString().getBytes(charEncoding);
return new ByteArrayInputStream(dataBytes);
}
public static void main(final String[] args) throws IOException {
String encoding = "UTF-8";
Map<String, String> valuesMap = new LinkedHashMap<String, String>();
InputStream is = getTestData(encoding);
new So2565230().fill(valuesMap, is, encoding);
for (Map.Entry<String, String> entry : valuesMap.entrySet()) {
System.out.format("K=[%s] V=[%s]%n", entry.getKey(), entry.getValue());
}
}
private void fill(Map<String, String> map, InputStream is, String charEncoding) throws IOException {
BufferedReader bufReader = new BufferedReader(new InputStreamReader(is, charEncoding));
for (String line = bufReader.readLine(); line != null; line = bufReader.readLine()) {
Matcher m = rgx.matcher(line);
if (!m.matches()) {
System.err.println("Line has improper format (" + line + ")");
continue;
}
String key = m.group(1);
String value = m.group(2);
if (map.put(key, value) != null) {
System.err.println("Duplicate key detected: (" + line + ")");
}
}
}
}
答案 6 :(得分:-1)
但是,我担心您必须定义要解析的文本文件的格式。例如,第一列和第二列之间的单独字符是什么,如果没有修复,则会导致更多困难。