2010-12-02 2 views
4

J'essaie de générer plusieurs listes de données, de longueur variable, dans un fichier CSV. Chaque liste doit être une colonne dans le fichier CSV de sortie. Y a-t-il une façon simple de faire les choses? Si je produisais chaque liste en tant que ligne, je bouclerais juste chaque liste et produirais un retour quand j'atteindrais la fin, mais cette approche ne fonctionne pas en travaillant par colonne. J'ai pensé à parcourir toutes les listes à la fois, élément par élément et incrémenter un compteur, mais cela échouerait aussi parce que certaines listes sont plus longues que d'autres. Pour y remédier je devrais vérifier à chaque itération si le compteur est passé la fin de chaque liste, ce qui serait assez cher en termes de calculs.Existe-t-il un moyen facile de générer un CSV par colonne?

Merci pour vos idées!

+1

Et que se passerait-il si deux listes avaient une longueur différente? Entrée vide? –

+0

La plupart du coût est d'écrire à IO, comment vous le faites est peu susceptible d'être important. Je vous suggère de l'écrire comme vous le vouliez et de ne pas vous soucier des performances (en supposant que vous ayez un tampon sensible) –

Répondre

2

Je pense que cela est assez straight-forward: (escapes)

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

sortie (assez laid depuis son aléatoire):

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  ,   ,  
+0

C'est essentiellement ce que j'avais codé, mais le bit de pré-normalisation est intéressant. Cela signifie que j'ai seulement besoin de vérifier N fois au lieu de N^2 fois. Merci! – ahugenerd

+0

et si la chaîne contient une virgule? –

+0

@ peter.murray.rust: corrigé maintenant, l'implémentation précédente ne fixait que la largeur de la colonne, maintenant elle ajoute des virgules (et "échappe" les caractères spéciaux - [\ n | \ "|,]) – dacwe

2

Il est utile d'avoir un regard sur http://commons.apache.org/sandbox/csv/

Cette références aussi quelques autres bibliothèques CSV.

Notez que de nombreuses réponses n'ont pas pris en compte les chaînes contenant des virgules. C'est la raison pour laquelle les bibliothèques valent mieux que de le faire vous-même.

+0

+1 pour avoir été le premier à suggérer une bibliothèque csv. Comment se fait-il que tout le monde pense que générer/analyser csv est facile mais personne n'écrirait un analyseur XML? – whiskeysierra

+0

En fait, j'ai codé des parseurs XML. Ces données doivent en fait être générées sous forme de fichier CSV par colonne pour d'autres personnes. – ahugenerd

+0

Merci pour le lien! On dirait que OpenCSV est plutôt sympa. – ahugenerd

1

Créer un tableau d'itérateurs (un pour chaque liste). Puis faire une boucle sur le tableau, en vérifiant si l'itérateur hasNext(); Si c'est le cas, affichez . La sortie des virgules et des retours à la ligne est triviale. Arrêtez lorsque tous les itérateurs sont retournés hasNext()==false.

1

Vous pouvez utiliser String.format():

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

Le résultat sera une largeur de colonne fixe de 4 caractères - aussi longtemps que les valeurs utilisées sont plus courtes. Sinon, la mise en page va se casser.

a, bb, ccc 
aaa, b, c 
1

Je ne suis pas familier avec Java du tout, mais si vous avez un type de données orienté matrix, vous pouvez remplir les lignes en utilisant looping facile, puis transposer, puis écrire à l'aide looping facile. Votre routine d'impression peut gérer des entrées nulles en produisant une chaîne nulle, ou des espaces de largeur fixe si vous préférez.

+0

C'est ce que je pensais initialement. Je ne sais pas si cela serait efficace sur le plan des calculs, en particulier avec le volume de données que je dois produire. Si l'opération de transposition est O (1) ou O (logN) alors cela pourrait en valoir la peine. Je vais regarder. – ahugenerd

0

Vous pouvez faire quelque chose comme ceci:

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

Pour le traitement CSV Je l'ai utilisé cette bibliothèque à plusieurs reprises: http://www.csvreader.com/java_csv.php très simple et pratique.

Cheerz!

0

Je devrais vérifier à chaque itération si le compteur est passé la fin de chaque liste, ce qui serait assez cher en termes de calculs.

Traversez-le. Ce sera, de façon réaliste, faible par rapport au coût de la réalisation de l'itération, qui à son tour sera minuscule par rapport au coût d'écriture d'un morceau donné de texte dans le fichier. Au moins, en supposant que vous avez des conteneurs d'accès aléatoire.

Mais vous ne devriez pas penser en termes de compteur et d'indexation de toute façon; vous devriez penser en termes d'itérateurs (qui contourner la question à accès aléatoire et simplifier le code).

+0

Le surmonter est certainement très utile dans de nombreux cas. Malheureusement, il ne répond toujours pas à la question. Cela montre simplement que vous n'êtes pas d'accord avec sa prémisse. – ahugenerd

0

Si vous vouliez faire dans une paire de boucles et une méthode, vous pourriez faire ce qui suit. En tant qu'exercice, vous pourriez le faire avec une boucle, mais ce ne serait pas aussi clair que de savoir ce qu'il fait.

En utilisant les données d'échantillon de @dacwe, cette méthode prend 10 us (micro-secondes).

Questions connexes