我正在尝试使用Java中的Scanner类扫描数据字段以'@'或'endline'分隔的文件。这是一个示例输入文件:
Student @ Codey @ Huntting
Student @ Sarah @ Honsinger
要正确扫描如下所示的输入文件,我尝试将Java扫描程序上的分隔符更改为正则表达式"[@\\v]"
,该表达式应与@
或任何垂直空白匹配,根据{{3}}
\n
和\r
这是我用来测试的代码:
Scanner scanner = new Scanner(new File("data/initialize.txt"));
int tokenNum = 0;
scanner.useDelimiter("[@\\v]");
while(scanner.hasNext()) {
System.out.println("Token #" + tokenNum++ + ": " + scanner.next());
}
scanner.close();
我希望扫描的令牌是:
Token #1: Student
Token #2: Codey
Token #3: Huntting
Token #4: Student
Token #5: Sarah
Token #6: Honsinger
但是实际收到的令牌是:
Token #1: Student
Token #2: Codey
Token #3: Huntting
Token #4
Token #5: Student
Token #6: Sarah
Token #7: Honsinger
Token #8:
我希望扫描仪在扫描Huntting
之后,在Huntting之后会上升到换行符,并且在下次调用input.next()
时跳过该换行符,但是由于某种原因,扫描仪似乎在行尾抓住了一个空字符串。
我已经检查了多次,文件在任何行之后都没有空格。我尝试了[@[\\v]]
和[@][\\v]
之类的不同模式,但是这些模式始终会给数据带来相同的空字符串错误,或者输出完全不正确。
答案 0 :(得分:0)
如果我理解正确,我们可能只想删除@
和其后的空格,然后用新行替换并在其前面添加文本。也许此表达式会有所帮助:
([\S\s]*?)(?:@\s|$)
如果不需要此表达式,则可以在regex101.com中修改/更改表达式。
您还可以在jex.im中可视化您的表达式:
此代码段表明我们可能有一个有效的表达式:
const regex = /([\S\s]*?)(?:@\s|$)/gm;
const str = `Student @ Codey @ Huntting
Student @ Sarah @ Honsinger`;
const subst = `\n$1`;
// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);
console.log('Substitution result: ', result);
import java.util.regex.Matcher;
import java.util.regex.Pattern;
final String regex = "([\\S\\s]*?)(?:@\\s|$)";
final String string = "Student @ Codey @ Huntting\n"
+ "Student @ Sarah @ Honsinger";
final String subst = "$1\\n";
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);
// The substituted value will be contained in the result variable
final String result = matcher.replaceAll(subst);
System.out.println("Substitution result: " + result);
如果我们希望删除新行,则可能希望将其添加到第二个捕获组中,并且可以解决该问题:
([\s\S]+?)(@\s|\n\s|\n|$)
在这里,在第二个捕获组(@\s|\n\s|\n|$)
中,使用逻辑或,我们可以排除我们不希望拥有的字符:
const regex = /([\s\S]+?)(@\s|\n\s|\n|$)/gm;
const str = `Student @ Codey @ Huntting
Student @ Sarah @ Honsinger
`;
const subst = `Token #: $1\n`;
// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);
console.log('Substitution result: ', result);
import java.util.regex.Matcher;
import java.util.regex.Pattern;
final String regex = "([\\s\\S]+?)(@\\s|\\n\\s|\\n|$)";
final String string = "Student @ Codey @ Huntting\n\n"
+ "Student @ Sarah @ Honsinger \n";
final String subst = "Token #: $1\\n";
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);
// The substituted value will be contained in the result variable
final String result = matcher.replaceAll(subst);
System.out.println("Substitution result: " + result);
答案 1 :(得分:0)
您的文件可能包含\r\n
格式的换行符。
在这种情况下,扫描仪会找到定界符\r
并输出\r
之前的任何内容。然后,它找到定界符\n
,并在\r
和\n
之间输出空令牌,然后在\n
之后继续。
为了允许\r\n
中断,我建议您以完全相同的顺序使用\r\n|[@\v]
作为分隔符正则表达式。当然,转义后变成"\r\n|[@\\v]"
。
正如Andreas提到的,您可以使用的另一个正则表达式是@|\R
,因为\R
与任何Unicode换行符都匹配,包括\r\n
。这甚至可能是最好的解决方案。
答案 2 :(得分:0)
您的问题是换行符是一对maven-antlr-plugin
,并且\r\n
分别匹配它们。要复制此代码,让我们更改代码以将内联字符串用于测试数据:
\v
输出
String input = "Student @ Codey @ Huntting\r\n" +
"Student @ Sarah @ Honsinger\r\n";
try (Scanner scanner = new Scanner(input).useDelimiter("[@\\v]")) {
for (int tokenNum = 0; scanner.hasNext(); tokenNum++) {
System.out.println("Token #" + tokenNum + ": \"" + scanner.next() + "\"");
}
}
一种解决方法是尝试首先匹配Token #0: "Student "
Token #1: " Codey "
Token #2: " Huntting"
Token #3: ""
Token #4: "Student "
Token #5: " Sarah "
Token #6: " Honsinger"
Token #7: ""
对:
\r\n
输出
useDelimiter("\r\n|[@\\v]")
但是,这将花费时间检查Token #0: "Student "
Token #1: " Codey "
Token #2: " Huntting"
Token #3: "Student "
Token #4: " Sarah "
Token #5: " Honsinger"
两次,因此最好使用内置的\R
(任何Unicode换行符序列都等效于\r
)< / em>:
\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]
相同的结果,但更清楚地反映出您想要匹配的方式。
您当然可以使用useDelimiter("@|\\R")
或trim()
删除前导和尾随空格,但是为什么不让strip()
起作用呢?使用Scanner
需要一个(非捕获)组,以使其与空白匹配区分开:
|
输出
useDelimiter("\\s*(?:@|\\R)\\s*")