Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java:拆分逗号分隔的字符串,但忽略引号中的逗号_Java_Regex_String - Fatal编程技术网

Java:拆分逗号分隔的字符串,但忽略引号中的逗号

Java:拆分逗号分隔的字符串,但忽略引号中的逗号,java,regex,string,Java,Regex,String,我有一根细绳,大概是这样的: foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy" boolean foundQuote = false; if(charAtIndex(currentStringIndex) == '"') { foundQuote = true; } if(foundQuote == true) { //do nothing } else { string[] split = currentString.s

我有一根细绳,大概是这样的:

foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"
boolean foundQuote = false;

if(charAtIndex(currentStringIndex) == '"')
{
   foundQuote = true;
}

if(foundQuote == true)
{
   //do nothing
}

else 

{
  string[] split = currentString.split(',');  
}
我想用逗号分开,但我需要忽略引号中的逗号。我该怎么做?似乎regexp方法失败了;我想当我看到一个报价时,我可以手动扫描并进入一个不同的模式,但最好使用现有的库。(编辑:我想我指的是已经是JDK的一部分或者已经是Apache Commons等常用库的一部分的库。)

上述字符串应分为:

foo
bar
c;qual="baz,blurb"
d;junk="quux,syzygy"
注意:这不是一个CSV文件,它是一个包含在整体结构更大的文件中的单个字符串

请尝试:

public class Main { 
    public static void main(String[] args) {
        String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
        String[] tokens = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
        for(String t : tokens) {
            System.out.println("> "+t);
        }
    }
}
输出:

> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"
换句话说:仅当逗号前面有零个引号或偶数个引号时,才在逗号上拆分

或者,对眼睛来说更友好一点:

public class Main { 
    public static void main(String[] args) {
        String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";

        String otherThanQuote = " [^\"] ";
        String quotedString = String.format(" \" %s* \" ", otherThanQuote);
        String regex = String.format("(?x) "+ // enable comments, ignore white spaces
                ",                         "+ // match a comma
                "(?=                       "+ // start positive look ahead
                "  (?:                     "+ //   start non-capturing group 1
                "    %s*                   "+ //     match 'otherThanQuote' zero or more times
                "    %s                    "+ //     match 'quotedString'
                "  )*                      "+ //   end group 1 and repeat it zero or more times
                "  %s*                     "+ //   match 'otherThanQuote'
                "  $                       "+ // match the end of the string
                ")                         ", // stop positive look ahead
                otherThanQuote, quotedString, otherThanQuote);

        String[] tokens = line.split(regex, -1);
        for(String t : tokens) {
            System.out.println("> "+t);
        }
    }
}
这将产生与第一个示例相同的结果

编辑 正如@MikeFHay在评论中提到的:

我更喜欢使用,因为它有更合理的默认值(请参阅上面关于通过
String#split()
修剪空匹配的讨论),所以我:

Splitter.on(Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))

我会这样做:

foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"
boolean foundQuote = false;

if(charAtIndex(currentStringIndex) == '"')
{
   foundQuote = true;
}

if(foundQuote == true)
{
   //do nothing
}

else 

{
  string[] split = currentString.split(',');  
}

(上一个库的分支,它允许生成的输出在不运行Windows时具有Windows行终止符
\r\n


不要使用lookahead和其他疯狂的正则表达式,只需首先取出引号。也就是说,对于每个引号分组,将该分组替换为
\u IDENTIFIER\u 1
或其他一些指示符,并将该分组映射到字符串、字符串的映射


使用逗号拆分后,用原始字符串值替换所有映射的标识符。

尝试类似的
(?!\”、(?!\”
。这应该与
匹配,它们没有被
包围。

您正处于一个恼人的边界区域,regexp几乎无法完成(正如巴特所指出的那样,逃避引用会让生活变得艰难),但一个成熟的解析器似乎有些矫枉过正


如果您可能很快需要更高的复杂性,我会去找一个解析器库。例如,

我很不耐烦,选择不等待答案…作为参考,这样做看起来并不难(这适用于我的应用程序,我不需要担心转义引号,因为引号中的内容仅限于一些受约束的形式):

final静态私有模式splitSearchPattern=Pattern.compile(“[\”,]”);
私有列表按逗号分隔(字符串s){
如果(s==null)
返回集合。emptyList();
列表=新的ArrayList();
匹配器m=splitSearchPattern.Matcher;
int pos=0;
布尔quoteMode=false;
while(m.find())
{
字符串sep=m.group();
如果(“\”。等于(sep))
{
quoteMode=!quoteMode;
}
else如果(!quoteMode&&,“.equals(sep))
{
int-toPos=m.start();
列表。添加(s.子字符串(位置、拓扑));
pos=m.end();
}
}
如果(位置

(读者练习:通过查找反斜杠扩展到处理转义引号。)

虽然我通常喜欢正则表达式,但对于这种依赖于状态的标记化,我相信有一个简单的解析器(在本例中,它比这个词听起来简单得多)可能是一种更清洁的解决方案,尤其是在可维护性方面,例如:

String input = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
List<String> result = new ArrayList<String>();
int start = 0;
boolean inQuotes = false;
for (int current = 0; current < input.length(); current++) {
    if (input.charAt(current) == '\"') inQuotes = !inQuotes; // toggle state
    else if (input.charAt(current) == ',' && !inQuotes) {
        result.add(input.substring(start, current));
        start = current + 1;
    }
}
result.add(input.substring(start));
String input=“foo,bar,c;qual=”baz,blurb\”,d;junk=”qux,syzygy\”;
列表结果=新建ArrayList();
int start=0;
布尔inQuotes=false;
对于(int current=0;current
如果您不关心保留引号中的逗号,可以通过将引号中的逗号替换为其他逗号,然后在逗号处拆分来简化此方法(无需处理起始索引,无需最后一个字符的特殊情况):

String input = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
StringBuilder builder = new StringBuilder(input);
boolean inQuotes = false;
for (int currentIndex = 0; currentIndex < builder.length(); currentIndex++) {
    char currentChar = builder.charAt(currentIndex);
    if (currentChar == '\"') inQuotes = !inQuotes; // toggle state
    if (currentChar == ',' && inQuotes) {
        builder.setCharAt(currentIndex, ';'); // or '♡', and replace later
    }
}
List<String> result = Arrays.asList(builder.toString().split(","));
String input=“foo,bar,c;qual=”baz,blurb\”,d;junk=”qux,syzygy\”;
StringBuilder=新的StringBuilder(输入);
布尔inQuotes=false;
对于(int currentIndex=0;currentIndex
我不会建议Bart给出正则表达式的答案,我发现在这种特殊情况下解析解决方案更好(正如Fabian所建议的)。我已经尝试过正则表达式解决方案和自己的解析实现,我发现:

  • 解析要比使用带有反向引用的正则表达式进行拆分快得多—短字符串的解析速度约为20倍,长字符串的解析速度约为40倍
  • 正则表达式在最后一个逗号后找不到空字符串。但这不是最初的问题,这是我的要求
  • 我的解决方案和测试如下

    String tested = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\",";
    long start = System.nanoTime();
    String[] tokens = tested.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
    long timeWithSplitting = System.nanoTime() - start;
    
    start = System.nanoTime(); 
    List<String> tokensList = new ArrayList<String>();
    boolean inQuotes = false;
    StringBuilder b = new StringBuilder();
    for (char c : tested.toCharArray()) {
        switch (c) {
        case ',':
            if (inQuotes) {
                b.append(c);
            } else {
                tokensList.add(b.toString());
                b = new StringBuilder();
            }
            break;
        case '\"':
            inQuotes = !inQuotes;
        default:
            b.append(c);
        break;
        }
    }
    tokensList.add(b.toString());
    long timeWithParsing = System.nanoTime() - start;
    
    System.out.println(Arrays.toString(tokens));
    System.out.println(tokensList.toString());
    System.out.printf("Time with splitting:\t%10d\n",timeWithSplitting);
    System.out.printf("Time with parsing:\t%10d\n",timeWithParsing);
    
    String tested=“foo,bar,c;qual=”baz,blurb\”,d;junk=”qux,syzygy\”;
    长启动=System.nanoTime();
    String[]tokens=tested.split(“,(?=([^\”]*\“[^\”]*\”*[^\“]*\”*”)*[^\“]*$”);
    long-timeWithSplitting=System.nanoTime()-开始;
    start=System.nanoTime();
    List tokensList=new ArrayList();
    布尔inQuotes=false;
    StringBuilder b=新的StringBuilder();
    for(char c:tested.toCharArray()){
    开关(c){
    案例',':
    如果(以引号引){
    b、 附加(c);
    }否则{
    tokensList.add(b.toString());
    b=新的StringBuilder();
    }
    打破
    案例“\”:
    inQuotes=!inQuotes;
    违约:
    b、 附加(c);
    打破
    
    String[] a = p.matcher(input).results()
        .map(m -> m.group(m.start(1)<0? 2: 1))
        .toArray(String[]::new);
    
    for(Matcher m = p.matcher(input); m.find(); ) {
        String token = m.group(m.start(1)<0? 2: 1);
        System.out.println("found: "+token);
    }
    
    Pattern p = Pattern.compile("\\G((\"(.*?)\"|[^,])*),?");
    
    String s = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
    String[] split = s.split( "(?<!\".{0,255}[^\"]),|,(?![^\"].*\")" );