标记string,但忽略引号内的分隔符
我希望有以下string
!cmd 45 90 "An argument" Another AndAnother "Another one in quotes"
成为以下的数组
{ "!cmd", "45", "90", "An argument", "Another", "AndAnother", "Another one in quotes" }
我试过了
new StringTokenizer(cmd, "\"")
但这会返回“另一个”和“另一个”另一个“另一个”这不是预期的效果。
谢谢。
编辑:我又改变了这个例子,这次我相信它解释了最好的情况,虽然它没有比第二个例子不同。
在这种情况下使用java.util.regex.Matcher
并执行find()
而不是任何forms的split
要容易得多。
也就是说,不是为记号之间的分隔符定义模式,而是为记号本身定义模式。
这是一个例子:
String text = "1 2 \"333 4\" 55 6 \"77\" 8 999"; // 1 2 "333 4" 55 6 "77" 8 999 String regex = "\"([^\"]*)\"|(\\S+)"; Matcher m = Pattern.compile(regex).matcher(text); while (m.find()) { if (m.group(1) != null) { System.out.println("Quoted [" + m.group(1) + "]"); } else { System.out.println("Plain [" + m.group(2) + "]"); } }
上面的照片( 如在ideone.com上看到的 ):
Plain [1] Plain [2] Quoted [333 4] Plain [55] Plain [6] Quoted [77] Plain [8] Plain [999]
模式基本上是:
"([^"]*)"|(\S+) \_____/ \___/ 1 2
有两个交替:
- 第一个备用匹配开头的双引号,除了双引号之外的任何序列(在第1组中被捕获),然后是双引号
- 第二个replace与第2组中捕获的任何非空白字符序列匹配
- 交替的顺序在这种模式中很重要
请注意,这不会处理引用段中的转义双引号。 如果你需要这样做,那么模式变得更加复杂,但Matcher
解决scheme仍然有效。
参考
- regular-expressions.info/Brackets分组和捕捉 , 与垂直酒吧交替 , 字符类 , 重复星和加
也可以看看
- regular-expressions.info/Examples – Programmer – string – 用于带有转义引号的模式
附录
请注意, StringTokenizer
是一个遗留类 。 build议使用java.util.Scanner
或String.split
,当然也可以使用java.util.regex.Matcher
来获得最大的灵活性。
相关问题
- 弃用和传统API之间的区别?
- 扫描仪与StringTokenizer与String.Split
- 使用java.util.Scannervalidationinput – 有很多例子
以旧式的方式去做。 创build一个函数,查看for循环中的每个字符。 如果字符是一个空格,请将所有内容(不包括空格)作为条目添加到数组中。 注意位置,并再次做同样的事情,将一个空格后的下一个部分添加到数组中。 遇到双引号时,将名为“inQuote”的布尔值标记为true,并在inQuote为true时忽略空格。 当inQuote为true时,如果您点击引号,则将其标记为false,并在遇到空格时返回到分解事件。 然后,您可以根据需要扩展此function以支持转义字符等。
这可以用正则expression式来完成吗? 我猜,我不知道。 但是整个function写起来要比这个回复less。
以一种老式的方式:
public static String[] split(String str) { str += " "; // To detect last token when not quoted... ArrayList<String> strings = new ArrayList<String>(); boolean inQuote = false; StringBuilder sb = new StringBuilder(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (c == '"' || c == ' ' && !inQuote) { if (c == '"') inQuote = !inQuote; if (!inQuote && sb.length() > 0) { strings.add(sb.toString()); sb.delete(0, sb.length()); } } else sb.append(c); } return strings.toArray(new String[strings.size()]); }
我认为嵌套引号是非法的,而且空的标记可以省略。
你在这里的例子只需要被双引号字符分开。
这是一个老问题,但是这是我作为一个有限状态机的解决scheme。
高效,可预测,没有花哨的技巧。
100%的testing覆盖率。
拖放到您的代码。
/** * Splits a command on whitespaces. Preserves whitespace in quotes. Trims excess whitespace between chunks. Supports quote * escape within quotes. Failed escape will preserve escape char. * * @return List of split commands */ static List<String> splitCommand(String inputString) { List<String> matchList = new LinkedList<>(); LinkedList<Character> charList = inputString.chars() .mapToObj(i -> (char) i) .collect(Collectors.toCollection(LinkedList::new)); // Finite-State Automaton for parsing. CommandSplitterState state = CommandSplitterState.BeginningChunk; LinkedList<Character> chunkBuffer = new LinkedList<>(); for (Character currentChar : charList) { switch (state) { case BeginningChunk: switch (currentChar) { case '"': state = CommandSplitterState.ParsingQuote; break; case ' ': break; default: state = CommandSplitterState.ParsingWord; chunkBuffer.add(currentChar); } break; case ParsingWord: switch (currentChar) { case ' ': state = CommandSplitterState.BeginningChunk; String newWord = chunkBuffer.stream().map(Object::toString).collect(Collectors.joining()); matchList.add(newWord); chunkBuffer = new LinkedList<>(); break; default: chunkBuffer.add(currentChar); } break; case ParsingQuote: switch (currentChar) { case '"': state = CommandSplitterState.BeginningChunk; String newWord = chunkBuffer.stream().map(Object::toString).collect(Collectors.joining()); matchList.add(newWord); chunkBuffer = new LinkedList<>(); break; case '\\': state = CommandSplitterState.EscapeChar; break; default: chunkBuffer.add(currentChar); } break; case EscapeChar: switch (currentChar) { case '"': // Intentional fall through case '\\': state = CommandSplitterState.ParsingQuote; chunkBuffer.add(currentChar); break; default: state = CommandSplitterState.ParsingQuote; chunkBuffer.add('\\'); chunkBuffer.add(currentChar); } } } if (state != CommandSplitterState.BeginningChunk) { String newWord = chunkBuffer.stream().map(Object::toString).collect(Collectors.joining()); matchList.add(newWord); } return matchList; } private enum CommandSplitterState { BeginningChunk, ParsingWord, ParsingQuote, EscapeChar }
尝试这个:
String str = "One two \"three four\" five \"six seven eight\" nine \"ten\""; String[] strings = str.split("[ ]?\"[ ]?");
我不知道你想要做什么的上下文,但它看起来像你试图parsing命令行参数。 一般来说,这是非常棘手的所有逃脱的问题; 如果这是你的目标,我会亲自看看JCommander。
尝试这个:
String str = "One two \"three four\" five \"six seven eight\" nine \"ten\""; String strArr[] = str.split("\"|\s");
这有点棘手,因为你需要逃避双引号。 这个正则expression式应该使用空格(\ s)或双引号来标记string。
您应该使用String的split
方法,因为它接受正则expression式,而StringTokenizer
中的分隔符的构造方法参数不会。 在上面提供的内容结尾处,您可以添加以下内容:
String s; for(String k : strArr) { s += k; } StringTokenizer strTok = new StringTokenizer(s);