web-dev-qa-db-fra.com

Fractionnement d'un fichier csv avec des guillemets comme délimiteur de texte à l'aide de String.split ()

J'ai un fichier séparé par des virgules avec de nombreuses lignes similaires à celle ci-dessous.

Sachin,,M,"Maths,Science,English",Need to improve in these subjects.

Les guillemets sont utilisés pour échapper à la virgule de délimitation utilisée pour représenter plusieurs valeurs.

Maintenant, comment diviser la valeur ci-dessus sur le délimiteur de virgule en utilisant String.split() si cela est possible?

47
FarSh018
public static void main(String[] args) {
    String s = "Sachin,,M,\"Maths,Science,English\",Need to improve in these subjects.";
    String[] splitted = s.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
    System.out.println(Arrays.toString(splitted));
}

Sortie:

[Sachin, , M, "Maths,Science,English", Need to improve in these subjects.]
167
Achintya Jha

Comme votre problème/vos exigences ne sont pas si complexes, une méthode personnalisée peut être utilisée qui fonctionne plus de 20 fois plus rapidement et produit les mêmes résultats. Ceci est variable en fonction de la taille des données et du nombre de lignes analysées, et pour les problèmes plus complexes, l'utilisation d'expressions régulières est un must.

import Java.util.Arrays;
import Java.util.ArrayList;
public class SplitTest {

public static void main(String[] args) {

    String s = "Sachin,,M,\"Maths,Science,English\",Need to improve in these subjects.";
    String[] splitted = null;

 //Measure Regular Expression
    long startTime = System.nanoTime();
    for(int i=0; i<10; i++)
    splitted = s.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
    long endTime =   System.nanoTime();

    System.out.println("Took: " + (endTime-startTime));
    System.out.println(Arrays.toString(splitted));
    System.out.println("");


    ArrayList<String> sw = null;        
 //Measure Custom Method
            startTime = System.nanoTime();
    for(int i=0; i<10; i++)
    sw = customSplitSpecific(s);
    endTime =   System.nanoTime();

    System.out.println("Took: " + (endTime-startTime));
    System.out.println(sw);         
}

public static ArrayList<String> customSplitSpecific(String s)
{
    ArrayList<String> words = new ArrayList<String>();
    boolean notInsideComma = true;
    int start =0, end=0;
    for(int i=0; i<s.length()-1; i++)
    {
        if(s.charAt(i)==',' && notInsideComma)
        {
            words.add(s.substring(start,i));
            start = i+1;                
        }   
        else if(s.charAt(i)=='"')
        notInsideComma=!notInsideComma;
    }
    words.add(s.substring(start));
    return words;
}   

}

Sur mon propre ordinateur, cela produit:

Took: 6651100
[Sachin, , M, "Maths,Science,English", Need to improve in these subjects.]

Took: 224179
[Sachin, , M, "Maths,Science,English", Need to improve in these subjects.]
16

Si vos chaînes sont toutes bien formées, cela est possible avec l'expression régulière suivante:

String[] res = str.split(",(?=([^\"]|\"[^\"]*\")*$)");

L'expression garantit qu'un fractionnement ne se produit qu'à des virgules qui sont suivies d'un nombre pair (ou nul) de guillemets (et donc pas à l'intérieur de ces guillemets).

Néanmoins, il peut être plus facile d'utiliser un analyseur non regex simple.

6
Howard