Scanner delimter 在 java 中只取双引号之外的新行

Scanner delimter to only take new line outside of double quotes in java

提问人:Siva Ram 提问时间:11/15/2023 最后编辑:Siva Ram 更新时间:11/17/2023 访问量:101

问:

只有当双引号不在一侧时,我才需要根据新行从扫描仪中读取。

输入:"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

请建议

则表达式 java.util.scanner

评论

0赞 Cary Swoveland 11/15/2023
匹配怎么样?演示(表达式的第一个字符是空格。*\"[^\"]+\" *|[^\r\n\"]+
0赞 Siva Ram 11/15/2023
在第 2 行之后,然后 eah 值在“也是错误的是它,New conenet”\r\n Conetent 3“应该在一场比赛中
0赞 VGR 11/15/2023
也许可以做到这一点,但老实说,这对 Scanner 来说太复杂了。我会一个接一个地阅读字符,然后自己进行解析。
0赞 Cary Swoveland 11/16/2023
我现在看到我建议的正则表达式以与预期结果不同的方式拆分字符串,但我得出的结论是我不理解这个问题,特别是第一行。你能编辑一下,使那条线更精确吗?
0赞 Reilas 11/16/2023
这是有模式的,我忘记了名字。它类似于抽象语法树
1赞 AdrianHHH 11/17/2023
这与在 CSV 文件中的字段中处理逗号的问题相同。关于这个话题有几个问题。尝试在此网站上搜索 .[regex] csv comma is:q

答:

0赞 Patrick Janser 11/15/2023 #1

您可以做的是使用匹配的正则表达式 双引号字符串或 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/multipleg 标志:

/
(?:
  # 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 开发人员。我使用PHPJavaScript并这样做 替换为替换回调,如下所示:

// 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

没关系。。。但无论如何,不要忘记处理引号 字符串,因为它们不是关于这一点的任何细节或假设。

评论

0赞 Siva Ram 11/15/2023
这也是行不通的,因为正则表达式的期望只需要能够直接拆分,而不是在此基础上拧代码
0赞 Patrick Janser 11/15/2023
@SivaRam 我不认为你会设法用正则表达式来做到这一点,只是分成几行,不在字符串内。通常,人们会告诉你使用适当的解析器,例如 CSV。在这种情况下,没有传统或分隔符。我也不认为使用正则表达式环视可以解决问题,因为正如 Java 告诉你的那样,回溯应该有一个固定的长度。这不会解决双引号字符串中双引号字符的情况。需要防弹解决方案吗?然后用回调和一些逻辑来编写几行代码。\n\r;,
0赞 Reilas 11/16/2023 #2

遍历 s,当 bfalse 时,为任何换行符附加一个子字符串

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

评论

1赞 Patrick Janser 11/16/2023
如果字符串本身包含一些引号,这将不起作用:(由反斜杠转义)。我还认为问题的作者有义务使用这个类,这就是为什么我的回答也没有解决他的期望。"Something can be \"quoted\" in the string"Scanner
0赞 Reilas 11/17/2023
@PatrickJanser,一个有效的参数;我会把它加进去。