【问题标题】:How to split a string with a space and leave anything inside a quotation alone?如何用空格分割字符串并将任何内容单独留在引号内?
【发布时间】:2016-02-01 00:14:28
【问题描述】:

给定用户输入的字符串,我试图通过删除任何空格并获取每个标记来拆分字符串。

但是当我在引号中有一个标记时我遇到了困难。这里有一些例子可以更好地说明:

用户输入:that is cool

预期输出:

that
is
cool

用户输入:The book "Harry Potter" is cool

预期输出:

The
book
"Harry Potter"
is
cool

用户输入:Here " is one final " example

预期输出:

Here
"     is   one   final   "
example

这是我目前所拥有的:

public static void main(String[] args) {
    String input;
    Scanner in = new Scanner(System.in);
    System.out.print("User input: ");
    input = in.nextLine();
    input = input.trim();
    input = input.replaceAll("\\s+", " ");
    String[] a = input.split(" ");

    for (String c: a) {
        System.out.println(c);
    }
}

它仅适用于第一个示例,但对于带有引号的示例,它会将引号内的空格分开膨胀。示例 3 输出:

Here
"
is
one
final
"
example

【问题讨论】:

  • 报价标志,你应该对照报价检查每个字符,如果你遇到这种情况,你应该设置这个标志并继续寻找接近的报价。

标签: java regex string


【解决方案1】:

你能试试吗:

String str = "Here  \"     is   one   final   \"    example";
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
Matcher regexMatcher = regex.matcher(str);
while (regexMatcher.find()) {
  System.out.println(regexMatcher.group());
} 

【讨论】:

    【解决方案2】:

    你可以使用这个模式

    Pattern pattern = Pattern.compile("\"([^\"]+)\"|'([^']+)'|\\S+");
    

    匹配空格之间或引号之间的单词和它们之间的空格。它也可以使用单引号正确运行。它会将"it's" 保留为一个您可能想要也可能不想要的单词。

    然后您将像这样遍历所有匹配项

    Matcher matcher = pattern.matcher(input);
    while (matcher.find()) {
        System.out.println(matcher.group());
    }
    

    【讨论】:

      【解决方案3】:

      您可以尝试以下方法:

      public static void main (String[] args) {
          System.out.println(Arrays.toString(splitOnSpacesButNotOnStrings(
               "The       book   \"Harry Potter\"   is   cool"
          )));
          System.out.println(Arrays.toString(splitOnSpacesButNotOnStrings(
               "Here  \"     is   one   final   \"    example"
          )));
          // Output:
          // [The, book, "Harry Potter", is, cool]
          // [Here, "     is   one   final   ", example]
      }
      
      private static String[] splitOnSpacesButNotOnStrings(String s) {
          return s.split(" +(?=(?:(?:.*?\"){2})*[^\"]*$)");
      }
      

      不过,只有当你的字符串是平衡的,即包含偶数个 "s 时,它才会起作用。

      【讨论】:

        【解决方案4】:

        我能想到的唯一解决方案是编写一个小解析器,它简单地遍历您的输入字符串并保留一个标志,告诉您是否有一个开放引号。

        public static void main(String[] args)
        {
            String input = "Here  \"     is   one   final   \"    example";
            List<String> tokens = new ArrayList<>();
            boolean inQuote = false;
        
            input = input.trim();
            String token = "";
            for (char c : input.toCharArray())
            {
                if (c == ' ' && !inQuote)
                {
                    if (token.length() > 0)
                        tokens.add(token);
                    token = "";
                }
                else
                {
                    token += c;
                    if (c == '"')
                    {
                        inQuote = !inQuote;
                        if (!inQuote)
                        {
                            tokens.add(token);
                            token = "";
                        }
                    }
                }
            }
            if (token.length() > 0)
                tokens.add(token);
            System.out.println(tokens);
        }
        

        【讨论】:

          【解决方案5】:

          不要专注于你想split 的事情。结果更容易专注于您想要find 的事情:

          private static final Pattern p = Pattern.compile("\"[^\"]+\"|\\S+");
          //                                     quotes---  ^^^^^^^^^^ 
          //                                     non+whitespace        ^^^^ 
          public static List<String> splitTokensAndQuotes(String text) {
              List<String> result = new ArrayList<>();
              Matcher m = p.matcher(text);
              while (m.find()) {
                  result.add(m.group());
              }
              return result;
          } 
          

          演示:

          public static void main(String[] args) {
          
              splitTokensAndQuotes("that         is      cool")
                      .forEach(System.out::println);
              System.out.println("------");
          
              splitTokensAndQuotes("the       book   \"Harry Potter\"   is   cool")
                      .forEach(System.out::println);
              System.out.println("------");
          
              splitTokensAndQuotes("Here  \"     is   one   final   \"    example")
                      .forEach(System.out::println);
              System.out.println("------");
          
          }
          

          结果:

          that
          is
          cool
          ------
          the
          book
          "Harry Potter"
          is
          cool
          ------
          Here
          "     is   one   final   "
          example
          ------
          

          【讨论】:

          • 非常好的解决方案。我实际上正在考虑自己找到正确的正则表达式,但无法弄清楚。我唯一的批评是你没有充分的理由让它特定于 java 8。
          • @wvdz 谢谢:)。无论如何,Java 8 推出已经一年多了。老实说,我看不出有什么理由不使用它,因为如果有人不能使用 Java 8,forEach 可以很容易地使用 for 循环重写。
          【解决方案6】:

          这个呢:

          public static void main(String[] args) {
              StringTokenizer stk;
              //String s="that         is      cool";
              //String s="The       book   "Harry Potter"   is   cool";
              String s = "Here  \"     is   one   final   \"    example";
              Scanner scanner = new Scanner(s);
              scanner.useDelimiter(" +(?=(?:(?:.*?\\\"){2})*[^\\\"]*$)");
              while (scanner.hasNext()) {
                  System.out.println(scanner.next());
          
              }
          }
          

          【讨论】:

            【解决方案7】:

            以下是如何在一行中做到这一点:

            String[] terms = input.trim().split(" +(?=(([^\"]*\"){2})*[^\"]*$)");
            

            仅当不在引号内时才在空格上拆分,其中“不在引号内时”定义为“后跟 偶数个引号”。

            根据您的示例,对trim() 的调用是可选的,但会满足引导用户进入前导空格的情况。


            一些测试代码:

            String input = "Here  \"     is   one   final   \"    example";
            String[] terms = input.trim().split(" +(?=(([^\"]*\"){2})*[^\"]*$)");
            Arrays.stream(terms).forEach(System.out::println);
            

            输出:

            Here
            "     is   one   final   "
            example
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-01-28
              • 1970-01-01
              • 2016-01-07
              • 2015-09-03
              • 1970-01-01
              • 2012-08-03
              • 1970-01-01
              • 2018-10-23
              相关资源
              最近更新 更多