2010-12-02 88 views
4

我試圖輸出多個不同長度的數據列表到一個CSV文件。每個列表應該是輸出CSV文件中的一列。有沒有一種直接的做事方式?如果我將每個列表作爲一行輸出,我只需遍歷每個列表並在結束時輸出一個返回值,但在按列進行操作時此方法不起作用。有沒有簡單的方法來輸出列式CSV?

我想過一次一個一個地逐個檢查所有列表並增加一個計數器,但這也會失敗,因爲某些列表比其他列表長。爲了彌補這一點,我將不得不在每次迭代時檢查計數器是否超過每個列表的末尾,這在計算方面會相當昂貴。

感謝您的任何想法!

+1

如果兩個列表的長度不同,會發生什麼?空入境? – 2010-12-02 18:40:37

+0

大部分成本都是寫給IO的,你怎麼做都不太重要。我建議你按照你想要的方式編寫它,而不要擔心性能(假設你使用了合理的緩衝) – 2010-12-02 18:45:30

回答

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

這是值得擁有看看http://commons.apache.org/sandbox/csv/

這也引用了其他一些CSV庫。

請注意,很多答案沒有考慮包含逗號的字符串。這就是圖書館比自己做得更好的原因。

+0

+1是因爲第一次提示csv庫。大家如何認爲生成/解析csv很容易,但沒有人會寫一個XML解析器? – whiskeysierra 2010-12-02 19:16:47

+0

其實,我已經編碼了xml解析器。這些數據實際上需要以其他人的列式CSV格式輸出。 – ahugenerd 2010-12-02 20:01:28

+0

感謝您的鏈接!看起來像OpenCSV是相當不錯的。 – ahugenerd 2010-12-02 20:19:05

1

創建一個迭代器數組(每個列表一個)。然後遍歷數組,檢查迭代器hasNext();如果是這樣,則輸出iterator.next()。輸出逗號和換行符是微不足道的。當所有迭代器返回hasNext()==false時停止。

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 
1

我不熟悉Java可言,但如果你有一個matrix面向數據類型,你可以填補易於使用循環行,然後轉它,那麼它易於使用的循環寫出來的。您的打印例程可以通過輸出空字符串或固定寬度的空格來處理空條目(如果您願意的話)。

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!

0

我將不得不在每次迭代時檢查計數器是否超過每個列表的末尾,這在計算方面會相當昂貴。

克服它。實際上,與實際進行迭代的成本相比,這實際上會很小,與將任何給定位的文本寫入文件的成本相比,這反過來將是微小的。至少,假設你有隨機存取容器。

但是你不應該在計數器和索引方面思考;你應該用迭代器來思考(它避開了隨機訪問問題並簡化了代碼)。

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(微秒)。

相關問題