使用Java Scanner库解析此问题的最有效方法是什么?

时间:2010-08-17 13:05:49

标签: java performance java.util.scanner

我正在尝试用Java的Scanner库解析一个大文件的一部分,但是我很难确定解析这个文本的最佳路径。

SECTOR 199
FLAGS 0x1000
AMBIENT LIGHT 0.67
EXTRA LIGHT 0.00
COLORMAP 0
TINT 0.00 0.00 0.00
BOUNDBOX 7.399998 8.200002 6.199998 9.399998 8.500000 7.099998
COLLIDEBOX 7.605121 8.230770 6.200000 9.399994 8.469233 7.007693
CENTER 8.399998 8.350001 6.649998
RADIUS 1.106797
VERTICES 12
0: 1810
1: 1976
2: 1977
3: 1812
4: 1978
5: 1979
6: 1820
7: 1980
8: 1821
9: 1981
10: 1982
11: 1811
SURFACES 1893 8

它有一些可选字段(SOUND,COLLIDEBOX),所以我不能像我一直在处理文件的前一部分那样按特定顺序进行解析。我不确定如何在不降低效率的情况下做到这一点,目前我一直在考虑解析每一行,然后用String.split(“\ s +”)拆分它来获取值,但是我我很好奇我可能有什么其他选择。 :\

4 个答案:

答案 0 :(得分:2)

输入看起来很复杂,足以保证完整的解析器。我建议使用像ANTLR(http://www.antlr.org/)这样的库。

答案 1 :(得分:1)

我首先使用关键字定义枚举,例如:

 public enum Keyword {SECTOR, FLAGS, AMBIENT, EXTRA, COLORMAP, TINT, 
    BOUNDBOX, COLLIDEBOX, CENTER, RADIUS, VERTICES, SURFACES}

解析可以逐行完成,在空白字符处分割。然后我将第一个元素转换为Keyword类的枚举,并使用一个简单的开关构造来处理值:

public Model parse(List<String> lines) {

   Model model = new Model();

   Iterator<String> it = lines.iterator();
   while(it.hasNext()) {
      String[] elements = it.next().split("\s+");

      switch(Keyword.valueOf(elements[0])) {
        case SECTOR: model.addSector(elements[1]); break;
        case FLAGS: model.addFlags(elements[1]); break;
        // ...
        case VERTICES:
          int numberOfVertices = Integer.parseInt(elements[1]);
          for (int i = 0; i < numberOfVertices; i++) {
             elements = it.next().split("\s+");
             model.addVertice(i, elements[1]);
          }
          break;
        case default:
          // handle malformed line

      }
   }
   return model;
}

答案 2 :(得分:1)

这种方法怎么样:

find next command (SECTOR, FLAGS, AMBIENT LIGHT, EXTRA LIGHT, etc)
no command found? -> output error and stop
map to command implementation 
execute command (pass it the scanner and your state holder)
command impl handles specific reading of arguments
rinse, repeat,...

您必须创建一个Command接口:

public interface Command {
    String getName();
    void execute(Scanner in, ReadState state);
}

并为您可能遇到的每种类型的命令单独实现它:

public class SectorCommand implements Command {
    public String getName() {
        return "SECTOR";
    }
    public void execute(Scanner in, ReadState state) {
        state.setSector(in.nextInt());
    }
}

以及某种寻找命令的工厂:

public class CommandFactory {

    private Map<String, Command> commands;
    public CommandFactory() {
        commands = new HashMap<String, Command>();
        addCommand(new SectorCommand());
        // add other commands
    }
    public Command findCommand(Scanner in) {
        for (Map.Entry<String, Command> entry : commands.entrySet()) {
            if (in.findInLine(entry.getKey())) {
                return commands.get(entry.getValue);
            }
        }
        throw new IllegalArgumentException("No command found");
    }
    private void addCommand(Command command) {
        commands.put(command.getName(), command); 
    }
}

(此代码可能无法编译)

答案 3 :(得分:0)

如果文件非常大,我建议你可以使用java.io.RandomAccessFile,它可以跳过你要解析的任何区域,速度非常快。如果将整个文件映射到memnory,可能会降低应用程序的速度。

它可以替代使用java.util.StringTokenizer来拆分简单的case。例如,空格,逗号等。它比正则表达式更快。<​​/ p>