regex 扫描器定界符在java中只接受双引号之外的新行

oymdgrw7  于 12个月前  发布在  Java
关注(0)|答案(2)|浏览(135)

我有要求从扫描仪读取的基础上新的一行,只有当它的外面的双引号。
输入:"Content1 \r\n block" \r\n Contentn2 \r\n New Content " \r\n Conetent3"
预期输出:
“内容1\r\n块”
内容2
新内容“\r\n内容3”
我试过

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
请建议

dxxyhpgq

dxxyhpgq1#

你可以在两个不同的命名捕获组中使用一个正则表达式来匹配双引号字符串或你的\r\n。这可以通过使用(?: | )来执行“or”条件(不捕获它),然后使用(?<group_name> )来创建命名捕获组来完成。
要匹配双引号字符串,它可以是这样的:"(?:\\.|[^"])*"
解释:

  • "匹配开头的双引号。
  • \\.匹配一个后接任意字符的反斜杠。这是因为双引号在字符串中是允许的,并且通常用反斜杠转义。这样,我们就不会在双引号上停止。它也会正确处理这种情况:
Input : "Backslash = \\" or "Tab = \t"

字符串
如果我们只使用\\"作为模式,那么我们将无法正确匹配"Backslash = \\"字符串,并继续到下一个双引号。
这就是我们看到的使用正则表达式并不是一个真正的解决方案。我们需要像解析器一样“消费”内容。顺便说一下,你的内容看起来像一些没有通常的,;解析器的CSV内容。CSV解析器将正确处理字符串。我们必须尝试对正则表达式模式做同样的事情;-)

你必须检查双引号是如何在你的输入中转义的。例如,CSV语法只是像这样将引号加倍:

1997,Ford,E350,"Super ""luxurious"" truck"

  • [^"]将匹配任何不是双引号的字符。
  • (?: | )是一个非捕获组,有两种可能性。后面加上*意味着它可以重复0次或N次。

完整的正则表达式,带有 extended语法 * 的 * x标志,可以让你在正则表达式中添加注解和空格,以便更好地阅读。
PCRE语法(PHP),带有
g标志,表示*g/multiple

/
(?:
  # 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

**在regex 101 for Java 上测试:https://regex101.com/r/c0LZD2/3

你需要遍历匹配项并测试名为 newline 的组(或者索引为2的组)是否被填充。如果被填充,那么用你的真实的newline替换它。
我不是 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;
    }
  })
);

编辑

由于这个问题被编辑得更清楚了,我的答案不再有效了。最初的问题并没有说\r\n已经在 Java 字符串中,所以我实际上是在寻找这个4个字符的序列,而不是真实的回车符和新行字符。
也不清楚我们是否必须坚持使用Scanner类,并使用一种“拆分”模式。
没关系.但是在任何情况下,不要忘记处理字符串中的引号,因为它们不是关于这一点的任何细节或假设。

oyjwcjzk

oyjwcjzk2#

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

相关问题