是否有一种简单的方法来输出逐列CSV?

时间:2010-12-02 18:34:31

标签: java algorithm csv

我正在尝试将多个不同长度的数据列表输出到CSV文件。每个列表应该是输出CSV文件中的一列。有没有直接的做事方式?如果我将每个列表作为一行输出,我只是循环遍历每个列表并在结束时输出一个返回,但是这种方法在按列工作时不起作用。

我想要一次又一次地遍历所有列表,逐项并递增一个计数器,但这也会失败,因为有些列表比其他列表长。为了解决这个问题,我必须在每次迭代时检查计数器是否超过每个列表的末尾,这在计算方面相当昂贵。

感谢您的任何想法!

8 个答案:

答案 0 :(得分:2)

值得一看http://commons.apache.org/sandbox/csv/

这也引用了一些其他CSV库。

请注意,许多答案都没有考虑包含逗号的字符串。这就是为什么图书馆比自己做的更好的原因。

答案 1 :(得分:2)

我认为这很简单:

public static void main(String... args) throws IOException {

    ArrayList<ArrayList<String>> rows = getRandomData();

    if (rows.size() == 0)
        throw new RuntimeException("No rows");

    // normalize data
    int longest = 0;
    for (List<String> row : rows)
        if (row.size() > longest)
            longest = row.size();

    for (List<String> row : rows)
        while (row.size() < longest)
            row.add("");

    if (longest == 0)
        throw new RuntimeException("No colums");

    // fix special characters
    for (int i = 0; i < rows.size(); i++)
        for (int j = 0; j < rows.get(i).size(); j++)
            rows.get(i).set(j, fixSpecial(rows.get(i).get(j)));

    // get the maximum size of one column
    int[] maxColumn = new int[rows.get(0).size()];

    for (int i = 0; i < rows.size(); i++)
        for (int j = 0; j < rows.get(i).size(); j++)
            if (maxColumn[j] < rows.get(i).get(j).length())
                maxColumn[j] = rows.get(i).get(j).length();

    // create the format string
    String outFormat = "";
    for (int max : maxColumn)
        outFormat += "%-" + (max + 1) + "s, ";
    outFormat = outFormat.substring(0, outFormat.length() - 2) + "\n";

    // print the data
    for (List<String> row : rows)
        System.out.printf(outFormat, row.toArray());

}

private static String fixSpecial(String s) {

    s = s.replaceAll("(\")", "$1$1");

    if (s.contains("\n") || s.contains(",") || s.contains("\"") || 
            s.trim().length() < s.length()) {
        s = "\"" + s + "\"";
    }

    return s;
}

private static ArrayList<ArrayList<String>> getRandomData() {

    ArrayList<ArrayList<String>> data = new ArrayList<ArrayList<String>>();

    String[] rand = { "Do", "Re", "Song", "David", "Test", "4", "Hohjoh", "a \"h\" o", "tjo,ad" };
    Random r = new Random(5);

    for (int i = 0; i < 10; i++) {

        ArrayList<String> row = new ArrayList<String>();

        for (int j = 0; j < r.nextInt(10); j++)
            row.add(rand[r.nextInt(rand.length)]);

        data.add(row);
    }

    return data;
}

输出(非常丑陋,因为它是随机的)(escapes):

Re       , 4           , "tjo,ad" , "tjo,ad" ,    
"tjo,ad" , "a ""h"" o" ,          ,          ,    
Re       , "a ""h"" o" , Hohjoh   , "tjo,ad" , 4  
4        , David       ,          ,          ,    
4        , Test        , "tjo,ad" , Hohjoh   , Re 
Do       , Hohjoh      , Test     ,          ,    
Hohjoh   , Song        ,          ,          ,    
4        , Song        ,          ,          ,    
4        , Do          , Song     , Do       ,    
Song     , Test        , Test     ,          ,    

答案 2 :(得分:1)

创建一个迭代器数组(每个列表一个。)然后遍历数组,检查迭代器hasNext();如果是,输出iterator.next()。输出逗号和换行是微不足道的。当所有迭代器都返回hasNext()==false时停止。

答案 3 :(得分:1)

您可以使用String.format():

System.out.println(String.format("%4s,%4s,%4s", "a", "bb", "ccc"));
System.out.println(String.format("%4s,%4s,%4s", "aaa", "b", "c"));

结果将是4个字符的固定列宽 - 只要使用的值更短。否则布局会中断。

   a,  bb, ccc
 aaa,   b,   c

答案 4 :(得分:1)

我根本不熟悉Java,但是如果你有一个面向matrix的数据类型,你可以使用简单的循环填充行,然后转置它,然后使用简单的循环将其写出来。您的打印例程可以通过输出空字符串来处理空条目,或者如果您愿意,可以处理固定宽度空格。

答案 5 :(得分:0)

您可以这样做:

List<List<?>> listOfLists = new LinkedList<List<?>>(); 
List<Iterator<?>> listOfIterators = new LinkedList<Iterator<?>>(); 
for (List<?> aList : listOfLists) {
         listOfIterators.add(aList.iterator()); 
}        
boolean done = false;        
while(!done) 
{   
      done = true;  
      for (Iterator<?> iter : listOfIterators)  
      {         
          if (iter.hasNext())       
          {             
             Object obj = iter.next();          
             //PROCESS OBJ          
             done = false;      
          }         
          else      
          {             
             //PROCESS EMPTY ELEMENT        
          }     
       } 
}

对于CSV处理,我多次使用过这个库:http://www.csvreader.com/java_csv.php非常简单方便。

Cheerz!

答案 6 :(得分:0)

  

我必须在每次迭代时检查计数器是否超过每个列表的末尾,这在计算方面相当昂贵。

克服它。实际上,与实际进行迭代的成本相比,这将是很小的,与将任何给定的文本位写入文件的成本相比,这反过来会很小。至少,假设你有随机访问容器。

但你不应该考虑计数器和索引;你应该考虑迭代器(避开随机访问问题并简化代码)。

答案 7 :(得分:0)

如果您想在一对循环和一种方法中执行此操作,则可以执行以下操作。

public static void writeCSV(PrintWriter pw, List<List<String>> columnsRows) {
    for(int i=0;;i++) {
        StringBuilder line = new StringBuilder();
        boolean empty = true;
        for (List<String> column : columnsRows) {
            String text = i < column.size() ? column.get(i) : "";
            found &= i >= column.size();
            if (text.contains(",") || text.contains("\"") || text.contains("\n") || text.trim() != text)
                text = '"' + text.replaceAll("\"", "\"\"") + '"';
            line.append(text).append(',');
        }
        if (empty) break;
        pw.println(line.substring(0, line.length()-1));
    }
}

作为一个练习,你可以用一个循环来完成这个练习,但是它不会像它做的那样清楚。

使用来自@dacwe的样本数据,此方法需要10 us(微秒)。