Самый эффективный способ разобрать это с библиотекой Java 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 ответа

Решение

Ввод выглядит так, как будто он достаточно сложен, чтобы гарантировать полноценный парсер. Я бы порекомендовал использовать такую ​​библиотеку, как ANTLR ( http://www.antlr.org/).

Сначала я определил бы перечисление с ключевыми словами, например:

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

Разбор может быть сделан построчно, разделяясь на пробельные символы. Затем я преобразовал бы первый элемент в перечисление из класса Keyword и использовал бы простую конструкцию switch для обработки значений:

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;
}

Как насчет этого подхода:

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,...

Вам нужно будет создать командный интерфейс:

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); 
    }
}

(этот код может не скомпилироваться)

Если файл очень большой, я предлагаю использовать java.io.RandomAccessFile, он может пропустить любую область, которую вы хотите проанализировать, и это очень быстро. Если вы отобразите весь файл в память, это может замедлить работу вашего приложения.

Альтернативно использовать java.util.StringTokenizer для разделения простого регистра. Например, пробел, запятая и так далее. Это быстрее, чем регулярное выражение.

Другие вопросы по тегам