使用分隔符分割带引号的字符串

7
我想使用空格作为分隔符来拆分字符串,但它应该能够智能地处理带引号的字符串。例如,对于像以下这样的字符串:
"John Smith" Ted Barry 

它应该返回三个字符串John Smith、Ted和Barry。

2
你可能需要先拆分引号包含的字符串,然后再按空格拆分其余部分。这里肯定有一些关于如何执行第一步的问题。第二步很简单。 - jahroy
1
你尝试过什么? - Basilio German
2
一个不错的CSV解析库会很适合你。大多数库都允许选择分隔符,并且会尊重并避免拆分带引号的文本。 - Hovercraft Full Of Eels
4
当你只有奇数个引号时会遇到问题。如果出现这种情况,你想要做什么? - Basilio German
1
我有一段(真的)糟糕的代码很久以前写的。我不记得它是否适用于所有情况,但应该经历了很多错误输入。我没有时间整理代码,请忽略与cmdId相关的任何内容:http://pastebin.com/aZngu65y。 - nhahtdh
5个回答

10

折腾了一下后,你可以使用正则表达式来完成这个任务。在以下内容上运行相当于“匹配所有”的操作:

((?<=("))[\w ]*(?=("(\s|$))))|((?<!")\w+(?!"))

一个Java示例:

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class Test
{ 
    public static void main(String[] args)
    {
        String someString = "\"Multiple quote test\" not in quotes \"inside quote\" \"A work in progress\"";
        Pattern p = Pattern.compile("((?<=(\"))[\\w ]*(?=(\"(\\s|$))))|((?<!\")\\w+(?!\"))");
        Matcher m = p.matcher(someString);

        while(m.find()) {
            System.out.println("'" + m.group() + "'");
        }
    }
}

输出:

'Multiple quote test'
'not'
'in'
'quotes'
'inside quote'
'A work in progress'

以上示例中使用的正则表达式详细解析可以在此处查看:

http://regex101.com/r/wM6yT9


尽管如此,正则表达式并不应该成为所有问题的首选解决方案 - 我只是随便试了试而已。这个例子涉及到很多边缘情况,例如处理Unicode字符、符号等。在这种情况下,最好使用经过验证的库。在使用本方法之前,请先查看其他答案。


我不确定输入是否包含Unicode,但您的代码将无法处理它。 - nhahtdh
这是一个很好的例子。+1,为什么不加一个if语句来检查m.group()是否返回空格,那样你就不必输出空格了。 - Basilio German
不行,当有两个带引号的字符串时它不能正常工作。Unicode问题仍然存在(并且u标志是Unicode大小写敏感的,与Unicode匹配无关)。 - nhahtdh
1
据我理解,文档中不需要使用(?u)。可以尝试使用\p{L}代替\w,它可以匹配任何Unicode字母。 - nhahtdh
Matt使用Apache commons-lang库的答案更简洁,更安全。 - Zoltán
@Zoltán我同意。这是一个社区维基回答,但我会稍微整理一下,并注意到正则表达式并不是访问此问题的唯一解决方案。 - Jay

4
尝试使用这段丑陋的代码。
    String str = "hello my dear \"John Smith\" where is Ted Barry";
    List<String> list = Arrays.asList(str.split("\\s"));
    List<String> resultList = new ArrayList<String>();
    StringBuilder builder = new StringBuilder();
    for(String s : list){
        if(s.startsWith("\"")) {
            builder.append(s.substring(1)).append(" ");
        } else {
            resultList.add((s.endsWith("\"") 
                    ? builder.append(s.substring(0, s.length() - 1)) 
                    : builder.append(s)).toString());
            builder.delete(0, builder.length());
        }
    }
    System.out.println(resultList);     

过多的空格会导致程序生成空字符串。 - nhahtdh
@nhahtdh:没错。我只是提供了一个提示,实际上并不是100%的工作解决方案。Trevor Senior 解决得很好。尽管那也有空格的问题,但这不是真正的问题,可以很容易地解决。 - Adeel Ansari
他实际上遇到了Unicode的问题,而且过多的空格会生成空字符串。 - nhahtdh
1
+1 在你的回答中学到了一些正则表达式知识。通过修复愚蠢的正则表达式错误,解决了我在空格和 Unicode 支持方面遇到的问题。这一切都归结于 *+ 的区别。 - Jay
@TrevorSenior:实际上,我不知道为什么想出了那个愚蠢的正则表达式。否则,只有 \\s 就足够了。我已经修复了这个问题。 - Adeel Ansari
啊,好的。我一直在想 && 是什么意思,但你也把它删掉了。 - Jay

3

好的,我写了一个小片段来实现你想要的功能以及更多其他的东西。由于你没有指定更多的条件,所以我没有费太多心思。我知道这种方法不太规范,你可能可以用一些已经存在的工具获得更好的结果。但是出于编程的乐趣,这里有一个例子:

    String example = "hello\"John Smith\" Ted Barry lol\"Basi German\"hello";
    int wordQuoteStartIndex=0;
    int wordQuoteEndIndex=0;

    int wordSpaceStartIndex = 0;
    int wordSpaceEndIndex = 0;

    boolean foundQuote = false;
    for(int index=0;index<example.length();index++) {
        if(example.charAt(index)=='\"') {
            if(foundQuote==true) {
                wordQuoteEndIndex=index+1;
                //Print the quoted word
                System.out.println(example.substring(wordQuoteStartIndex, wordQuoteEndIndex));//here you can remove quotes by changing to (wordQuoteStartIndex+1, wordQuoteEndIndex-1)
                foundQuote=false;
                if(index+1<example.length()) {
                    wordSpaceStartIndex = index+1;
                }
            }else {
                wordSpaceEndIndex=index;
                if(wordSpaceStartIndex!=wordSpaceEndIndex) {
                    //print the word in spaces
                    System.out.println(example.substring(wordSpaceStartIndex, wordSpaceEndIndex));
                }
                wordQuoteStartIndex=index;
                foundQuote = true;
            }
        }

        if(foundQuote==false) {
            if(example.charAt(index)==' ') {
                wordSpaceEndIndex = index;
                if(wordSpaceStartIndex!=wordSpaceEndIndex) {
                    //print the word in spaces
                    System.out.println(example.substring(wordSpaceStartIndex, wordSpaceEndIndex));
                }
                wordSpaceStartIndex = index+1;
            }

            if(index==example.length()-1) {
                if(example.charAt(index)!='\"') {
                    //print the word in spaces
                    System.out.println(example.substring(wordSpaceStartIndex, example.length()));
                }
            }
        }
    }

这还可以检查在引号之前或之后没有用空格分隔的单词,例如在“John Smith”之前的单词“hello”和在“Basi German”之后的单词。

当字符串被修改为"John Smith" Ted Barry时,输出结果为三个字符串, 1)“John Smith” 2)Ted 3)Barry

示例中的字符串为hello"John Smith" Ted Barry lol"Basi German"hello,并打印出以下结果: 1)hello 2)"John Smith" 3)Ted 4)Barry 5)lol 6)"Basi German" 7)hello

希望能帮到你


1
这是所有代码中最好的一个。它可以处理Unicode输入,并且在存在过多空格时不会生成空字符串。它将保持引号内的所有内容完整(这可能是优点或缺点)。我认为可以稍微修改一下代码以去除引号。进一步扩展可以是:添加对转义引号的支持。 - nhahtdh
当然,引号可以去掉。我只是故意加上引号。我已经在哪里添加了注释以删除引号。 - Basilio German

1

commons-lang库有一个StrTokenizer类可以为您执行此操作,还有java-csv库。

使用StrTokenizer的示例:

String params = "\"John Smith\" Ted Barry"
// Initialize tokenizer with input string, delimiter character, quote character
StrTokenizer tokenizer = new StrTokenizer(params, ' ', '"');
for (String token : tokenizer.getTokenArray()) {
   System.out.println(token);
}

输出:

John Smith
Ted
Barry

@BasilioGerman 我添加了一个例子,你可以考虑删除你的评论。 - Zoltán

1

这是我自己的版本,从http://pastebin.com/aZngu65y(在评论中发布)清理而来。 它可以处理Unicode。它将清除所有多余的空格(即使在引号中)- 这可能是好事或坏事,具体取决于需要。不支持转义引号。

private static String[] parse(String param) {
  String[] output;

  param = param.replaceAll("\"", " \" ").trim();
  String[] fragments = param.split("\\s+");

  int curr = 0;
  boolean matched = fragments[curr].matches("[^\"]*");
  if (matched) curr++;

  for (int i = 1; i < fragments.length; i++) {
    if (!matched)
      fragments[curr] = fragments[curr] + " " + fragments[i];

    if (!fragments[curr].matches("(\"[^\"]*\"|[^\"]*)"))
      matched = false;
    else {
      matched = true;

      if (fragments[curr].matches("\"[^\"]*\""))
        fragments[curr] = fragments[curr].substring(1, fragments[curr].length() - 1).trim();

      if (fragments[curr].length() != 0)
        curr++;

      if (i + 1 < fragments.length)
        fragments[curr] = fragments[i + 1];
    }
  }

  if (matched) { 
    return Arrays.copyOf(fragments, curr);
  }

  return null; // Parameter failure (double-quotes do not match up properly).
}

用于对比的样例输入:

"sdfskjf" sdfjkhsd "hfrif ehref" "fksdfj sdkfj fkdsjf" sdf sfssd


asjdhj    sdf ffhj "fdsf   fsdjh"
日本語 中文 "Tiếng Việt" "English"
    dsfsd    
   sdf     " s dfs    fsd f   "  sd f   fs df  fdssf  "日本語 中文"
""   ""     ""
"   sdfsfds "   "f fsdf

(第二行为空,第三行是空格,最后一行格式不正确)请根据您自己的预期输出进行判断,因为它可能会有所变化,但基线是第一个案例应返回[sdfskjf,sdfjkhsd,hfrif ehref,fksdfj sdkfj fkdsjf,sdf,sfssd]。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接