拆分嵌套字符串保持引号
我正在开发一个Java项目,需要嵌套字符串。
对于纯文本的输入字符串如下所示:
这是“一个字符串”,这是“一个”嵌套的“字符串”
结果必须如下:
[0] This [1] is [2] "a string" [3] and [4] this [5] is [6] "a \"nested\" string"
请注意 ,我希望保留\"
序列。
我有以下方法:
public static String[] splitKeepingQuotationMarks(String s);
我需要通过给定的规则从给定的s
参数创建一个字符串数组,而不使用Java Collection Framework或其衍生物。
我不确定如何解决这个问题。
是否可以使用正则表达式来解决这个问题?
根据评论中的问题进行更新 :
- 每个未转义的
"
关闭未结束"
(它们是平衡的) - 如果我们想要创建表示它的文字(为了创建代表
\
我们需要将其写为\\
文本),每个转义字符\
也必须被转义。
您可以使用以下正则表达式:
"[^"\\]*(?:\\.[^"\\]*)*"|\S+
请参阅正则表达式演示
Java演示 :
String str = "This is \"a string\" and this is \"a \\\"nested\\\" string\""; Pattern ptrn = Pattern.compile("\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|\\S+"); Matcher matcher = ptrn.matcher(str); while (matcher.find()) { System.out.println(matcher.group(0)); }
说明 :
-
"[^"\\]*(?:\\.[^"\\]*)*"
– 双引号后跟任何0+字符而不是"
和\
([^"\\]
)随后是0+序列的任何转义序列(\\.
),后跟除了"
和"
之外的任何0+字符 -
|
– 要么… -
\S+
– 一个或多个非空白字符
注意
@ Pshemo的建议 – "\"(?:\\\\.|[^\"])*\"|\\S+"
(或"\"(?:\\\\.|[^\"\\\\])*\"|\\S+"
会更正确) – 是相同的表达式,但效率低得多,因为它使用的是用*
量化的交替组。 由于正则表达式引擎必须测试每个位置,因此该构造涉及更多回溯,并且每个位置有2个概率。 我的基于unroll-the-loop的版本将立即匹配文本块,因此更加快速和可靠。
UPDATE
由于需要String[]
类型作为输出,因此您需要分两步执行:计算匹配项,创建数组,然后再次重新运行匹配器:
int cnt = 0; String str = "This is \"a string\" and this is \"a \\\"nested\\\" string\""; Pattern ptrn = Pattern.compile("\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|\\S+"); Matcher matcher = ptrn.matcher(str); while (matcher.find()) { cnt++; } System.out.println(cnt); String[] result = new String[cnt]; matcher.reset(); int idx = 0; while (matcher.find()) { result[idx] = matcher.group(0); idx++; } System.out.println(Arrays.toString(result));
查看另一个IDEONE演示
有效的另一种正则表达方法使用负面的后观:“单词”( \w+
) 或 “ 引用后跟任何前面没有反斜杠的下一个引号 ”,并将你的匹配设置为“全局”(不要第一场比赛的回归)
(\w+|".*?(?
在这看到它 。
另一种不使用正则表达式的方法:
import java.util.ArrayList; import java.util.Arrays; public class SplitKeepingQuotationMarks { public static void main(String[] args) { String pattern = "This is \"a string\" and this is \"a \\\"nested\\\" string\""; System.out.println(Arrays.toString(splitKeepingQuotationMarks(pattern))); } public static String[] splitKeepingQuotationMarks(String s) { ArrayList results = new ArrayList<>(); StringBuilder last = new StringBuilder(); boolean inString = false; boolean wasBackSlash = false; for (char c : s.toCharArray()) { if (Character.isSpaceChar(c) && !inString) { if (last.length() > 0) { results.add(last.toString()); last.setLength(0); // Clears the sb } } else if (c == '"') { last.append(c); if (!wasBackSlash) inString = !inString; } else if (c == '\\') { wasBackSlash = true; last.append(c); } else last.append(c); } results.add(last.toString()); return results.toArray(new String[results.size()]); } }
输出:
[这是,“一个字符串”,而且,这是“一个”嵌套的“字符串”]