【问题标题】:How should I parse this simple text file in Java?我应该如何用 Java 解析这个简单的文本文件?
【发布时间】:2010-04-02 06:03:42
【问题描述】:

我有一个如下所示的文本文件:

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

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

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

最好的方法是什么?

【问题讨论】:

    标签: java parsing hashmap java.util.scanner


    【解决方案1】:

    我会这样做!自 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。以小鹿斑比的臭鼬命名。

    最后一点:因为 close() 可以抛出 IOException,这就是我真正关闭流的方式:

    } finally {
      try {
        if (br != null) br.close();
      } finally {
        try {
          if (isr != null) isr.close();
        } finally {
          if (fin != null) fin.close();
        }
      }
    }
    

    【讨论】:

    • +1 将是我的解决方案 + LinkedHashMap,很好! “有一句话让我特别自豪” -> 大声笑
    • 您不能假设输入文件是用 UTF-8 编码的。它应该是一个参数。
    【解决方案2】:

    基于@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); 
      } 
    }
    

    【讨论】:

      【解决方案3】:

      我不知道最好的方法,但我怀疑最有效的方法是一次读取一行(使用BufferedReader),然后通过找到第一个空白字符来拆分每一行,拆分在那里,然后修剪两侧。但是,除非需要超快,否则无论您最喜欢什么都可以。

      我个人倾向于一次加载整个文件......除了它假设有足够的内存来保存整个文件之外,它不允许任何并行计算(例如,如果输入是从管道进来的)。能够在输入仍在生成时对其进行处理是有意义的。

      【讨论】:

      • 是的,我已经用 BufferedReaders 和 Scanners 做了一些测试,并且使用 BufferedReader 并自己进行拆分(不使用 String 的 .split() 方法)比 Scanner 快得多,但是 BufferedReader + String.split() 的速度差不多。无论哪种方式,它都需要做更多的工作,而且大多数时候可能不值得。
      • @Brendan,我建议手动拆分(而不是使用 String.split 函数),但你是对的......这可能没有显着差异。
      【解决方案4】:

      使用 Scanner 或普通的 FileReader + String.split() 应该都可以正常工作。我认为速度差异很小,除非您打算一遍又一遍地读取一个非常大的文件,否则没关系。

      编辑:实际上,对于第二种方法,使用BufferedReader。它有一个 getLine() 方法,这让事情变得更简单了。

      【讨论】:

        【解决方案5】:

        如果您想遵循教科书的解决方案,请使用 StringTokenizer。它直截了当,易于学习且非常简单。它可以克服简单的结构偏差(可变数量的空白字符、不均匀的格式行等)

        但是,如果您的文本被认为是 100% 格式良好且可预测的,那么只需将一堆行读入缓冲区,一次取一个,然后将部分字符串取出到您的 HashMap 键中,然后价值。它比 StringTokenizer 更快,但缺乏灵活性。

        【讨论】:

        • StringTokenizer 如果不是完全弃用,至少根据它的 JavaDoc 被认为是遗留类,不再是教科书的解决方案。
        • @Thilo:你应该改用什么?
        • 根据JavaDoc,String.split.
        【解决方案6】:

        缓存一个正则表达式怎么样? (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 + ")");
                    }
                }
            }
        }
        

        【讨论】:

          【解决方案7】:

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

          但是恐怕您必须定义要解析的文本文件的格式。比如你的第一列和第二列之间的分隔符是什么,如果不固定,会造成一些困难。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-11-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-07-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多