Java中Scanner仅以双引号外换行符为分隔符的实现方案
问题:Java Scanner仅将双引号外的换行符作为分隔符
需求是使用Java的Scanner读取内容时,只把双引号外部的换行符(\r\n或\n)当作分隔符。
输入示例:
"Content1 \r\n block" \r\n Contentn2 \r\n New Content " \r\n Conetent3"
预期输出:
"Content1 \r\n block"
Content2
New Content " \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); System.out.println(rec); }
但未得到预期结果。另外在JavaScript中使用正则/(?<=^[^"]*(?:"[^"]*"[^"]*)*)\r?\n/实现了该逻辑,但Java中作为Scanner分隔符时,抛出“Look-behind group does not have obvious maximum length”错误。
可行解决方案
方案1:使用Matcher遍历匹配目标片段
Java的Scanner分隔符不支持变长lookbehind,但可以通过Pattern和Matcher直接匹配所有符合要求的片段,跳过双引号内的换行符。核心思路是:匹配双引号包裹的内容(包含内部换行)或双引号外的非换行内容,组合成完整的分割单元。
代码示例:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class Main { public static void main(String[] args) { String content = "\"Content1 \r\n block\" \r\n Contentn2 \r\n New Content \" \r\n Conetent3\""; // 正则规则:匹配双引号包裹内容,或非引号非换行内容 Pattern pattern = Pattern.compile("\"[^\"]*\"|[^\"\\r\\n]+"); Matcher matcher = pattern.matcher(content); StringBuilder currentSegment = new StringBuilder(); while (matcher.find()) { String part = matcher.group(); // 处理双引号包裹的内容 if (part.startsWith("\"")) { currentSegment.append(part); } else { // 处理非引号内容,去除多余空格 currentSegment.append(part.trim()); } // 检查是否到达换行分隔位置 int nextPos = matcher.end(); if (nextPos >= content.length() || content.charAt(nextPos) == '\r' || content.charAt(nextPos) == '\n') { // 跳过后续的换行符 while (nextPos < content.length() && (content.charAt(nextPos) == '\r' || content.charAt(nextPos) == '\n')) { nextPos++; } System.out.println(currentSegment.toString().trim()); currentSegment.setLength(0); } } // 处理最后一个剩余片段 if (currentSegment.length() > 0) { System.out.println(currentSegment.toString().trim()); } } }
方案2:预处理内容,替换双引号内的换行符后拆分
先把双引号内部的换行符替换成临时占位符,再用Scanner按换行符拆分,最后还原占位符为换行符。
代码示例:
import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Main { public static void main(String[] args) { String content = "\"Content1 \r\n block\" \r\n Contentn2 \r\n New Content \" \r\n Conetent3\""; // 选一个不会出现在内容中的临时占位符 String placeholder = "###INNER_NEWLINE###"; // 替换双引号内的所有换行符 Pattern quotePattern = Pattern.compile("\"([^\"]*)\""); Matcher quoteMatcher = quotePattern.matcher(content); StringBuffer processedContent = new StringBuffer(); while (quoteMatcher.find()) { String quotedContent = quoteMatcher.group(1) .replace("\r\n", placeholder) .replace("\n", placeholder); quoteMatcher.appendReplacement(processedContent, "\"" + quotedContent + "\""); } quoteMatcher.appendTail(processedContent); // 按换行符拆分内容,再还原占位符 Scanner scanner = new Scanner(processedContent.toString()); scanner.useDelimiter("\\r?\\n"); while (scanner.hasNext()) { String segment = scanner.next().trim() .replace(placeholder, "\r\n"); System.out.println(segment); } scanner.close(); } }
为什么之前的方法失效?
- 你尝试的
findWithinHorizon正则[^"\r\n]+(?:\"[^\"]*\"[^"\r\n]+)*无法处理双引号内的换行,因为[^"\r\n]+会排除换行符,导致双引号内的内容被截断。 - JavaScript支持的变长lookbehind正则在Java中不被兼容,Java要求lookbehind组必须是固定长度(或有限可选长度,比如
(?<=a{1,3})),而你使用的正则是无限变长的,因此抛出错误。
内容的提问来源于stack exchange,提问作者Siva Ram
相关产品推荐
相关产品推荐

