提问人:Siva Ram 提问时间:11/15/2023 最后编辑:Siva Ram 更新时间:11/17/2023 访问量:101
Scanner delimter 在 java 中只取双引号之外的新行
Scanner delimter to only take new line outside of double quotes in java
问:
只有当双引号不在一侧时,我才需要根据新行从扫描仪中读取。
输入:"Content1 \r\n block" \r\n Contentn2 \r\n New Content " \r\n Conetent3"
预期输出 :
“Content1 \r\n 块”
内容2
新内容 “ \r\n Conetent3”
我试过
String content = "\"Content1 \r\n block\" \r\n Contentn2 \r\n New conenet \" \r\n Conetent 3\"";
Scanner fileScanner = new Scanner(content);
String regex = "[^\"\r\n]+(?:\"[^\"]*\"[^\"\r\n]+)*";
while(fileScanner.hasNext())
{
String rec = fileScanner.findWithinHorizon(regex,0);
Sysyetem.out.println(rec);
}
但它没有像上面预期的那样工作,也检查了其他,但没有一个为此工作
/(?<=^[^"]*(?:"[^"]*"[^"]*)*)\r?\n/
这在 Javascript 中工作,只能在 \r\n 之外工作,但在 java 中不起作用,当尝试用作 fileScanner.useDelimter() 时会引发错误 Look-behind group does not have obvuios maximum length
请建议
答:
您可以做的是使用匹配的正则表达式
双引号字符串或 ur ,在两个不同的名称中
捕获组。这可以通过使用 to do 来完成
“or”条件(不捕获它),然后 with 创建命名的捕获组。\r\n
(?: | )
(?<group_name> )
要匹配双引号字符串,它可以是这样的:"(?:\\.|[^"])*"
解释:
"
匹配开头的双引号。\\.
匹配后跟任何字符的反斜杠。这是 因为字符串中允许使用双引号,并且通常 用反斜线逃脱了。这样,我们就不会停留在双倍上 报价。它还将正确处理这种情况:Input : "Backslash = \\" or "Tab = \t"
如果我们只有 as 模式,那么我们就不会匹配 正确字符串并继续直到 下一个开头双引号。
\\"
"Backslash = \\"
这就是我们看到使用带有环视的正则表达式的地方 并不是真正的解决方案。我们需要“消费”像这样的内容 解析器就可以了。顺便说一句,您的内容看起来像一些 没有常用或分隔符的 CSV 内容。A CSV 解析器将正确处理字符串。我们必须尝试去做 我们的正则表达式模式也是如此;-)
,
;
您必须检查双引号是如何转义的 您的输入。例如,CSV 语法只是将 像这样的报价:
1997,Ford,E350,"Super ""luxurious"" truck"
[^"]
将匹配任何不是双引号的字符。(?: | )
是一个具有两种可能性的非捕获组。 添加后面意味着它可以重复 0 或 N 次。*
完整的正则表达式,带有 e x 趋向语法的x 标志,可让您在正则表达式中添加注释和空格,以获得更好的效果 读数。
PCRE 语法 (PHP),带有 g lobal/multiple 的 g 标志:
/
(?:
# String with possible escaped char inside.
(?<string>"(?:\\.|[^"])*")
| # or
# \r\n, but outside a string (as it's tested after the string).
(?<newline>\\r\\n)
)
/gx
在 Regex101 for Java 上测试它:https://regex101.com/r/c0LZD2/3
您必须遍历匹配项,并测试名为换行符的组(或索引 2 的组)是否已填充。如果它已填充 然后用真正的换行符替换它。
我不是 Java 开发人员。我使用PHP或JavaScript并这样做 替换为替换回调,如下所示:
// Same regular expression, but here without named capturing groups.
//
// g1 = string g2 = newline & spaces
// /¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\ /¯¯¯¯¯¯¯¯¯¯¯¯\
const regex = /(?:("(?:\\.|[^"])*")|(\s*\\r\\n\s*))/g;
const input = `Input : "Content1 \\r\\n block" \\r\\n Contentn2 \\r\\n New conenet " \\r\\n Conetent 3"
Input : "A string can contain \\"quotes\\"" \\r\\n Something else "\\" \\r\\n"
Input : "Tab = \\t | bell = \\a (\\"alert\\") | backslash = \\\\" \\r\\n "String 2" UnquotedString`;
console.log("input:");
console.log(input);
console.log("output:");
console.log(
// Replace input: callback match group 1 group 2
// | | | |
input.replace(regex, function(fullMatch, quotedString, newLine) {
// If the newLine group isn't empty, then replace it
// by a real new line.
if (newLine) {
return "\n";
// If not, don't change anything, so return the full match.
} else {
return fullMatch;
}
})
);
编辑
由于问题被编辑得更清楚一些,我的答案不是
再有效。最初的问题不是说那是
已经在 Java 字符串中,所以我实际上是在寻找这个
4 个字符的序列,而不是真正的回车和新车
行字符。\r\n
目前还不清楚我们是否必须坚持使用该类
并使用“拆分”模式。Scanner
没关系。。。但无论如何,不要忘记处理引号 字符串,因为它们不是关于这一点的任何细节或假设。
评论
\n\r
;
,
遍历 s,当 b 为 false 时,为任何换行符附加一个子字符串。
String s = "\"Content1 \r\n block\" \r\n Contentn2 \r\n New Content \" \r\n Conetent3\"";
List<String> l = new ArrayList<>();
char[] a = s.toCharArray();
boolean b = false;
int t = 0;
for (int i = 0, n = a.length; i < n; i++)
switch (a[i]) {
case '"' -> b = !b;
case '\n' -> {
if (!b) l.add(s.substring(t, (t = i) + 1).trim());
}
}
l.add(s.substring(t).trim());
这是输出,为了提高可读性,用文字替换。\r
\n
"Content1 \r\n block"
Contentn2
New Content " \r\n Conetent3"
编辑
如果希望使用转义引号,请在翻转 b 之前提供检查。\"
case '"' -> {
if (i == 0 || a[i - 1] != '\\') b = !b;
}
评论
"Something can be \"quoted\" in the string"
Scanner
评论
*\"[^\"]+\" *|[^\r\n\"]+
[regex] csv comma is:q