在引号外用逗号分隔[重复]

问题描述 投票:42回答:5

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

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

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

换句话说:子字符串qazxsw poi中的逗号不是分隔符。怎么处理这个?

在此先感谢..雅各布

java regex string split
5个回答
89
投票

你可以试试这个正则表达式:

"don't split, this"

这会将字符串拆分为str.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"); ,后跟偶数个双引号。换句话说,它在双引号之外的逗号上拆分。如果你的字符串中有平衡的引号,这将有效。

说明:

,

您甚至可以在代码中使用, // 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)

10
投票

当你可以匹配时为什么拆分?

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

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
                         );

这将匹配所有所需的片段("[^"]*"|[^,]+ )。

说明

  • 使用see demo,我们匹配完整的"[^"]*"
  • 或者"double-quoted strings"
  • 我们匹配|任何不是逗号的字符。

可能的改进是改进交替的字符串侧以允许引用的字符串包括转义引号。


1
投票

无需复杂的正则表达式,您可以轻松完成此操作:

  1. 拆分角色[^,]+。你得到一个字符串列表
  2. 处理列表中的每个字符串:拆分列表中偶数位置上的每个字符串(以零开始索引)“(在列表中得到一个列表),保留每个奇数位置的字符串(直接将其放入)列表中的列表)。
  3. 加入列表列表,这样您就只能获得一个列表。

如果你想处理'''的引用,你必须稍微调整算法(加入一些部分,你错误地拆分,或改变拆分为简单的正则表达式),但基本结构仍然存在。

所以基本上它是这样的:

"

承诺,这将与lambdas更加清洁!


0
投票

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

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);
            }
        }
    }
}

0
投票

基于@ zx81的答案,导致匹配的想法非常好,我添加了Java 9 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); } 调用,它返回一个results。由于OP想要使用Stream,我收集到了split,就像String[]那样。

如果你的逗号分隔符后面有空格(split),请注意。然后你需要改变模式。

Jshell演示

a, b, "c,d"

$ 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

说明

  1. Regex String so = "123,test,444,\"don't split, this\",more test,1"; Pattern.compile("\"[^\"]*\"|[^,]+") .matcher(so) .results() .map(MatchResult::group) .toArray(String[]::new); 匹配:报价,除报价之外的任何内容,报价。
  2. Regex [^"]匹配:报价,除报价0(或更多)次以外的任何内容,引用。
  3. 正则表达式需要首先“赢”,否则匹配任何东西,但逗号1次或更多次 - 即:[^"]* - 将“赢”。
  4. [^,]+需要Java 9或更高版本。
  5. 它返回results(),我使用Stream<MatchResult>调用映射并收集到字符串数组。无参数group()调用将返回toArray()
© www.soinside.com 2019 - 2024. All rights reserved.