我该如何用Java解析这个简单的文本文件?

时间:2010-04-02 06:03:43

标签: java parsing hashmap java.util.scanner

我有一个看起来像这样的文本文件:

grn129          agri-
ac-214          ahss
hud114          ahss
lov1150         ahss
lov1160         ahss
lov1170         ahss
lov1210         ahss

如果我想创建一个以第一列为键,第二列为值的HashMap,使用Java解析此文件的最佳方法是什么。

我应该使用Scanner类吗?尝试将整个文件作为字符串读入并拆分吗?

最好的方法是什么?

7 个答案:

答案 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)

朱利叶斯戴维斯的答案很好。

但是,我担心您必须定义要解析的文本文件的格式。例如,第一列和第二列之间的单独字符是什么,如果没有修复,则会导致更多困难。