【问题标题】:How to split a CSV input stream in java如何在java中拆分CSV输入流
【发布时间】:2016-05-23 04:18:27
【问题描述】:

我正在编写一个从标准输入读取 CSV 的 Java 应用程序。但是,我发现我在处理双引号时遇到了一些麻烦。

例如,如果我读到一个文本:

"He said, ""What?"""

输出给了我:

field[0] = `He said, What?"""'

最后两个引号是我不想要的。

这是我的代码:

public class Csv{
private BufferedReader fin;
private String fieldsep;
private ArrayList field;

public Csv(){
    this(System.in, ",");
}


public Csv(InputStream in, String sep){
    this.fin = new BufferedReader(new InputStreamReader(in));
    this.fieldsep = sep;
}


// getline: get one line, grow as needed
public String getline() throws IOException {
    String line;

    line = fin.readLine();
    if (line == null)
        return null;

    field = split(line, fieldsep);

    return line;
}

// split: split line into fields
private static ArrayList split(String line, String sep){
    ArrayList list = new ArrayList();
    int i, j;

    if (line.length() == 0)
        return list;

    i = 0;
    do {
        if (i < line.length() && line.charAt(i) == '"') {
            StringBuffer field = new StringBuffer();
            j = advquoted(line, ++i, sep, field);
            list.add(field.toString());
        } 

        else {
            j = line.indexOf(sep, i);
            if (j == -1)
                j = line.length();
            list.add(line.substring(i, j));
        }
        i = j + sep.length();
    } while (j < line.length());

    return list;
}

// advquoted: quoted field; return index of next separator
private static int advquoted(String s, int i, String sep, StringBuffer field){
    field.setLength(0);
    for ( ; i < s.length(); i++) {
        if (s.charAt(i) == '"' && ++i < s.length() && s.charAt(++i) != '"') {
            int j = s.indexOf(sep, i);
            if (j == -1)
                j = s.length();
            field.append(s.substring(i, j));
            i = j;
            break;
        }
        field.append(s.charAt(i));
    }

    return i;
}

【问题讨论】:

  • 我不确定,但对我来说很难通过你的代码。看来你已经解决了这个问题很尴尬。要从 CSV 中获取值,您可以使用例如 StringTokenizer 请参阅docs.oracle.com/javase/7/docs/api/java/util/…
  • 最后一个逗号是什么意思?
  • The last two commas are what I don't want. ...你的意思是最后两个引号不是你想要的吗?
  • 您知道已经有很好的 CSV 库可用吗?你有什么理由需要自己写吗?
  • 是的,应该是引号。这是我作业的一部分。我只是想制作自己的 cvs 类。

标签: java string csv split


【解决方案1】:

我想这应该没问题。

public class Csv{

   private BufferedReader fin;
   private String fieldsep;
   private ArrayList<String> field;

   public Csv(){
      this(System.in, ",");
   }


   public Csv(InputStream in, String sep){
      this.fin = new BufferedReader(new InputStreamReader(in));
      this.fieldsep = sep;
   }

   // getline: get one line, grow as needed
   public String getline() throws IOException {
      String line;
      line = fin.readLine();
      if (line == null)
        return null;
      field = split(line, fieldsep);
      return line;
   }

   // split: split line into fields
   private ArrayList split(String line, String sep){
      List<String> list = new ArrayList();
      StringTokenizer tokens = new StringTokenizer(line, sep, false);
      while (tokens.hasMoreElements()) {
            String next = (String) tokens.nextElement();
            next = next.trim().replaceAll("\"\"", "\"");
            list.add(next);
      }
      return list;
   }
}

结果是field = [He said, "What?"]

您应该考虑到 CSV 中的字段可以用双引号括起来。我不知道这是否会导致字段 "What?" 的多个 qoutes,但如果是这样,您应该知道所有字段都应该用双引号括起来。有关 CSV 格式的更多信息,请参阅https://www.ietf.org/rfc/rfc4180.txt

请注意,您永远不会关闭输入流!这可能会导致内存泄漏。如需更多信息,请参阅Closing BufferedReader and InputStreamReader

【讨论】:

  • 其实我正在上课。所以,你能修改我的方法吗?因为我必须遵循一些指示。
  • @RunningPig 我已经更新了我的答案并将我的解决方案包含在你的课程中。
【解决方案2】:

你让它变得非常复杂,只需使用StringTokenizer

String testString = "He said, \"\"What?\"\"";
        StringTokenizer st = new StringTokenizer(testString);
         while (st.hasMoreTokens()) {
             System.out.println(st.nextToken());
         }

输出:

He
said,
""What?""

现在你可以玩这些琴弦了。

【讨论】:

  • Emm,实际上我正在尝试制作 csv 类。
【解决方案3】:

正如其他人所建议的,您可以使用 StringTokenizer 让您的生活更轻松。 delimiters 应该是逗号和双引号,并且您希望 StringTokenizer 将分隔符返回给您。当分隔符是逗号时,该字段将是直到下一个逗号的所有内容。当分隔符是“时,该字段将是直到下一个”的所有内容。您可能想要修剪字段并从中删除前导和尾随 "。

【讨论】:

    【解决方案4】:

    正则表达式和流来救援。整个过程只需要一行:

    String[] terms = Arrays.stream(csv.split(",(?=(([^\"]*\"){2})*[^\"]*$"))
      .map(s -> s.replace("\"\"", "").toArray(String[]::new);
    

    【讨论】:

      猜你喜欢
      • 2012-02-02
      • 1970-01-01
      • 2012-05-12
      • 1970-01-01
      • 1970-01-01
      • 2020-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多