我希望能够解析如下字符串:“123456abcd9876az45678”。 BNF是这样的:
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 中是否已经存在任何可以帮助我的东西?
问题遗漏了太多信息。因此,所有答案都对问题有效,但对我的问题无效。
不幸的是,您不能在 Scanner 类 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));
}
要将扫描仪用作标记器,请使用
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;
}
}
}
在实际应用中,您可能应该预先编译模式。
问这个问题已经有很长一段时间了,OP 可能不再对答案感兴趣了,但是可能有一个零长度定界符.
var s = new Scanner(new ByteArrayInputStream("(2e-3*(4000+5)-.2)".getBytes(StandardCharsets.UTF_8)));
s.useDelimiter("(?=[+\\-*/()])|(?<=[+\\-*/()])");
while (s.hasNext()) {
System.out.print(s.next()+" ");
}
版画
( 2e - 3 * ( 4000 + 5 ) - .2 )
正则表达式技巧是:
(?=[+\\-*/()])
表示零长度后跟+-*/()
,我不得不双转义-
;(?<=[+\\-*/()])
表示+-*/()
之后的零长度;|
“或”如您所见,浮点数
2e-3
被拆分为 3 个标记。
您可以将分隔符设置为无法匹配任何内容的模式,例如
Scanner s = ...
s.useDelimiter("(?!=a)a");