java-用逗号分隔引号外

这个问题已经在这里有了答案:

  • Java:分割逗号分隔的字符串,但忽略引号中的逗号                                     10个答案

我的程序从文件中读取一行。 此行包含逗号分隔的文本,例如:

123,test,444,"don't split, this",more test,1

我希望拆分的结果是这样的:

123
test
444
"don't split, this"
more test
1

如果我使用String.split(","),则会得到以下信息:

123
test
444
"don't split
 this"
more test
1

换句话说:子字符串"don't split, this"中的逗号不是分隔符。 该如何处理?

提前致谢..雅各布

Jakob Mathiasen asked 2020-02-16T23:27:54Z
5个解决方案
104 votes

您可以尝试以下正则表达式:

str.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");

这会在(?x)上分割字符串,然后是双引号的偶数。 换句话说,它用双引号引起来的逗号分隔。 如果您在字符串中使用了引号,则此方法将起作用。

说明:

,           // Split on comma
(?=         // Followed by
   (?:      // Start a non-capture group
     [^"]*  // 0 or more non-quote characters
     "      // 1 quote
     [^"]*  // 0 or more non-quote characters
     "      // 1 quote
   )*       // 0 or more repetition of non-capture group (multiple of 2 quotes will be even)
   [^"]*    // Finally 0 or more non-quotes
   $        // Till the end  (This is necessary, else every comma will satisfy the condition)
)

您甚至可以在代码中使用正则表达式使用(?x)修饰符输入这样的内容。 修饰符会忽略您的正则表达式中的任何空格,因此读取分成多行的正则表达式变得更加容易,如下所示:

String[] arr = str.split("(?x)   " + 
                     ",          " +   // Split on comma
                     "(?=        " +   // Followed by
                     "  (?:      " +   // Start a non-capture group
                     "    [^\"]* " +   // 0 or more non-quote characters
                     "    \"     " +   // 1 quote
                     "    [^\"]* " +   // 0 or more non-quote characters
                     "    \"     " +   // 1 quote
                     "  )*       " +   // 0 or more repetition of non-capture group (multiple of 2 quotes will be even)
                     "  [^\"]*   " +   // Finally 0 or more non-quotes
                     "  $        " +   // Till the end  (This is necessary, else every comma will satisfy the condition)
                     ")          "     // End look-ahead
                         );
Rohit Jain answered 2020-02-16T23:36:15Z
11 votes

为什么在可以比赛时分手?

重提此问题是因为出于某种原因,没有提到简单的解决方案。 这是我们精美紧凑的正则表达式:

"[^"]*"|[^,]+

这将匹配所有所需的片段(请参见演示)。

说明

  • 使用[^,]+,我们匹配完整的"double-quoted strings"
  • [^,]+
  • 我们会匹配[^,]+任何非逗号的字符。

一种可能的改进是改进交替的字符串侧,以允许带引号的字符串包含转义的引号。

zx81 answered 2020-02-16T23:37:06Z
2 votes

您无需复杂的正则表达式即可非常轻松地执行此操作:

  1. 在字符"上拆分。您将获得字符串列表。
  2. 处理列表中的每个字符串:拆分列表中偶数位置上的每个字符串(从零开始索引),在“,”(列表中有列表)上,每个奇数位置的字符串都单独放置(直接将其放入 列表中的列表)。
  3. 加入列表列表,因此您只会得到一个列表。

如果要处理'“'的引号,则必须稍微修改一下算法(加入某些部分,您会错误地拆分或将拆分更改为简单的正则表达式),但是基本结构仍然存在。

所以基本上是这样的:

public class SplitTest {
    public static void main(String[] args) {
        final String splitMe="123,test,444,\"don't split, this\",more test,1";
        final String[] splitByQuote=splitMe.split("\"");
        final String[][] splitByComma=new String[splitByQuote.length][];
        for(int i=0;i<splitByQuote.length;i++) {
            String part=splitByQuote[i];
            if (i % 2 == 0){
               splitByComma[i]=part.split(",");
            }else{
                splitByComma[i]=new String[1];
                splitByComma[i][0]=part;
            }
        }
        for (String parts[] : splitByComma) {
            for (String part : parts) {
                System.out.println(part);
            }
        }
    }
}

承诺,这将使lambda更加干净!

stefan.schwetschke answered 2020-02-16T23:37:53Z
1 votes

基于@ zx81的答案,因为匹配的想法真的很不错,我添加了Java 9 Stream<MatchResult>调用,该调用返回Stream<MatchResult>。由于OP想要使用group(),所以我像27507686756104284284一样收集到toArray()

如果逗号分隔符后面有空格,请注意(Stream<MatchResult>)。 然后,您需要更改模式。

Jshell演示

$ jshell
-> String so = "123,test,444,\"don't split, this\",more test,1";
|  Added variable so of type String with initial value "123,test,444,"don't split, this",more test,1"

-> Pattern.compile("\"[^\"]*\"|[^,]+").matcher(so).results();
|  Expression value is: java.util.stream.ReferencePipeline$Head@2038ae61
|    assigned to temporary variable $68 of type java.util.stream.Stream<MatchResult>

-> $68.map(MatchResult::group).toArray(String[]::new);
|  Expression value is: [Ljava.lang.String;@6b09bb57
|    assigned to temporary variable $69 of type String[]

-> Arrays.stream($69).forEach(System.out::println);
123
test
444
"don't split, this"
more test
1

String so = "123,test,444,\"don't split, this\",more test,1";
Pattern.compile("\"[^\"]*\"|[^,]+")
    .matcher(so)
    .results()
    .map(MatchResult::group)
    .toArray(String[]::new);

说明

  1. 正则表达式Stream<MatchResult>匹配:引号,除引号外的任何内容,引号。
  2. 正则表达式Stream<MatchResult>匹配:引号,除引号0(或更多)倍以外的任何内容,一个引号。
  3. 该正则表达式需要首先赢得“胜利”,否则匹配逗号(除逗号以外)以外的任何其他项(即:Stream<MatchResult>)将“赢得胜利”。
  4. Stream<MatchResult>需要Java 9或更高版本。
  5. 它返回Stream<MatchResult>,我使用group()调用将其映射并收集到字符串数组。 无参数toArray()调用将返回Object[]
LIttle Ancient Forest Kami answered 2020-02-16T23:38:54Z
0 votes

请参见以下代码段。 此代码仅考虑快乐流程。 根据您的要求更改

public static String[] splitWithEscape(final String str, char split,
        char escapeCharacter) {
    final List<String> list = new LinkedList<String>();

    char[] cArr = str.toCharArray();

    boolean isEscape = false;
    StringBuilder sb = new StringBuilder();

    for (char c : cArr) {
        if (isEscape && c != escapeCharacter) {
            sb.append(c);
        } else if (c != split && c != escapeCharacter) {
            sb.append(c);
        } else if (c == escapeCharacter) {
            if (!isEscape) {
                isEscape = true;
                if (sb.length() > 0) {
                    list.add(sb.toString());
                    sb = new StringBuilder();
                }
            } else {
                isEscape = false;
            }

        } else if (c == split) {
            list.add(sb.toString());
            sb = new StringBuilder();
        }
    }

    if (sb.length() > 0) {
        list.add(sb.toString());
    }

    String[] strArr = new String[list.size()];

    return list.toArray(strArr);
}
Abhijith Nagarajan answered 2020-02-16T23:50:46Z
translate from https://stackoverflow.com:/questions/18893390/splitting-on-comma-outside-quotes