Сканер без разделителя

Я хотел бы иметь возможность анализировать строки, подобные следующим: «123456abcd9876az45678». БНФ выглядит так:

number: ? definition of an int ?
word: letter { , letter }
expression: number { , word , number }

Однако класс java.util.scanner не позволяет мне делать следующее:

Scanner s = new Scanner("-123456abcd9876az45678");
System.out.println(s.nextInt());
while (s.hasNext("[a-z]+")) {
    System.out.println(s.next("[a-z]+"));
    System.out.println(s.nextInt());
}

В идеале это должно дать:

-123456
abcd
987
az
45678

Я очень надеялся, что java.util.Scanner мне поможет, но, похоже, мне придется создать свой собственный сканер. Есть ли что-нибудь, что уже есть в Java API, что может мне помочь?


Вопрос пропустить слишком много информации. И поэтому все ответы справедливы для вопроса, но не для моей проблемы.


person Olivier Grégoire    schedule 25.01.2011    source источник
comment
Я не знаю, что должен делать этот код, но я думаю, что вы должны использовать [az]* вместо [az]   -  person Jean-Bernard Pellerin    schedule 26.01.2011
comment
хорошо, полный общий случай - это следующее 4d8 - 1d4+20, которое следует анализировать как два броска кубиков + константа. Может быть больше бросков костей, может не быть ни одного, могут быть пробелы или нет. Суть в том, что я хотел бы менять токены на лету без каких-либо разделителей. Я также не хочу, чтобы меня перенаправляли в обычный поток нотации игральных костей в SO, так как это не помогает мне со всеми этими функциями eval, которые они все используют. Я хочу построить дерево выражения игры в кости.   -  person Olivier Grégoire    schedule 26.01.2011


Ответы (4)


К сожалению, вы не можете использовать разделители с классом сканера AFAIK. Если вы хотите игнорировать разделители, вам нужно использовать методы, которые делают это, такие как findInLine() или findWithinHorizon(). В вашем случае подойдет findWithinHorizion().

Scanner s = new Scanner("-123456abcd9876az45678");
Pattern num = Pattern.compile("[+-]?\\d+");
Pattern letters = Pattern.compile("[A-Za-z]+");
System.out.println(s.findWithinHorizon(num, 0));
String str;
while ((str = s.findWithinHorizon(letters, 0)) != null) {
    System.out.println(str);
    System.out.println(s.findWithinHorizon(num, 0));
}
person Jeff Mercado    schedule 25.01.2011
comment
Ну, хорошая идея, но я не могу построить язык с этим. Я имею в виду, что если я ищу ‹число›, а затем снова ‹число›, он пропустит все ‹буквы›, чтобы найти число. Я думаю, мне придется сделать свой собственный сканер для этого. - person Olivier Grégoire; 26.01.2011
comment
@Frór: он не сильно отличается от примера, который вы нам дали, за исключением того, что он работает в соответствии с вашей спецификацией. Если, конечно, вы не упустили какую-то другую деталь, которая вам требуется. - person Jeff Mercado; 26.01.2011
comment
Да, есть и другие требования, которые я считал тривиальными. Сейчас я думаю об удалении всего этого вопроса и воссоздании нового с полным обзором проблемы. - person Olivier Grégoire; 26.01.2011

Чтобы использовать сканер в качестве токенизатора, используйте findWithinHorizon с \G для сканирования только с начала группы (= текущая позиция).

Пример поддержки пробелов (по запросу в комментариях):

Scanner scanner = new Scanner(input);
while (true) {
  String letters = scanner.findWithinHorizon("\\G\\s*\\[a-zA-Z]+", 0);
  if (letters != null) {
    System.out.println("letters: " + letters.trim());
  } else {
    String number = scanner.findWithinHorizon("\\G\\s[+-]?[0-9]+", 0);
    if (number != null) {
      System.out.println("number: " + number.trim());
    } else if (scanner.findWithinHorizon("\\G\\s*\\Z", 0) != null) {
      System.out.println("end");
      break;
    } else {
      System.out.println("unrecognized input");
      break;
    }
  }
}

В реальных приложениях вам, вероятно, следует компилировать шаблоны заранее.

person Stefan Haustein    schedule 14.03.2016

Этого можно добиться с помощью шаблона и классы Matcher . См. этот пример. .

person Amir Afghani    schedule 25.01.2011
comment
Нет, регулярное выражение этого не сделает. Смотрите мой комментарий под вопросом. - person Olivier Grégoire; 26.01.2011
comment
Ваш комментарий не дает мне понять, почему идиома Pattern/Matcher недостаточна. - person Amir Afghani; 26.01.2011
comment
Извините, чтобы быть полным, я хочу что-то лучше, что-то более податливое, чем регулярное выражение. Регулярное выражение, в моем случае, полностью лишает гибкости, которую я ожидаю. Все равно спасибо ! - person Olivier Grégoire; 26.01.2011

Вы можете установить разделитель на шаблон, который не может соответствовать чему-либо, например.

Scanner s = ...
s.useDelimiter("(?!=a)a");
person Steven R Brandt    schedule 14.03.2016