Java >> Java tutorial >  >> Tag >> String

Ignorer kommaer i anførselstegn ved opdeling af en kommasepareret streng

1. Oversigt

Når du behandler tekst, der indeholder komma-separerede-værdier, kan det være nødvendigt at ignorere kommaer, der forekommer i anførselstegn under-strenge.

I dette selvstudium vil vi undersøge forskellige metoder til at ignorere kommaer inden for anførselstegn, når du opdeler en kommasepareret streng .

2. Problemformulering

Antag, at vi skal opdele følgende kommaseparerede input:

String input = "baeldung,tutorial,splitting,text,\"ignoring this comma,\"";

Efter at have opdelt dette input og udskrevet resultatet, ville vi forvente følgende output:

baeldung
tutorial
splitting
text
"ignoring this comma,"

Med andre ord kan vi ikke betragte alle kommategn som værende separatorer. Vi skal ignorere de kommaer, der forekommer inden i citerede understrenge.

3. Implementering af en simpel parser

Lad os skabe en simpel parsingalgoritme:

List<String> tokens = new ArrayList<String>();
int startPosition = 0;
boolean isInQuotes = false;
for (int currentPosition = 0; currentPosition < input.length(); currentPosition++) {
    if (input.charAt(currentPosition) == '\"') {
        isInQuotes = !isInQuotes;
    }
    else if (input.charAt(currentPosition) == ',' && !isInQuotes) {
        tokens.add(input.substring(startPosition, currentPosition));
        startPosition = currentPosition + 1;
    }
}

String lastToken = input.substring(startPosition);
if (lastToken.equals(",")) {
    tokens.add("");
} else {
    tokens.add(lastToken);
}

Her starter vi med at definere en Liste kaldet tokens , som er ansvarlig for at gemme alle de kommaseparerede værdier.

Dernæst itererer vi over tegnene i input String .

I hver loop-iteration skal vi kontrollere, om det aktuelle tegn er et dobbelt anførselstegn . Når et dobbelt anførselstegn er fundet, bruger vi isInQuotes flag for at angive, at alle kommende kommaer efter de dobbelte anførselstegn skal ignoreres. isInQuotes flag vil blive sat falsk, når vi finder omsluttende dobbelte anførselstegn.

Et nyt token vil blive føjet til tokenserne liste, når isInQuotes er falsk, og vi finder et kommategn. Det nye token vil indeholde tegnene fra startPosition indtil den sidste position før kommategnet.

Derefter den nye startPosition vil være positionen efter kommategnet.

Endelig, efter løkken, har vi stadig det sidste token, der går fra startPosition til den sidste position af input. Derfor bruger vi substring() metode til at få det. Hvis dette sidste token kun er et komma, betyder det, at det sidste token skal være en tom streng. Ellers tilføjer vi det sidste token til tokens liste.

Lad os nu teste parsingkoden:

String input = "baeldung,tutorial,splitting,text,\"ignoring this comma,\"";
var matcher = contains("baeldung", "tutorial", "splitting", "text", "\"ignoring this comma,\"");
assertThat(splitWithParser(input), matcher);

Her har vi implementeret vores parsingkode i en statisk metode kaldet splitWithParser . Derefter definerer vi i vores test en simpel test input indeholdende et komma omgivet af dobbelte anførselstegn. Dernæst bruger vi hamcrest-testrammerne til at skabe en contains matcher for det forventede output. Til sidst bruger vi assertThat testmetode for at kontrollere, om vores parser returnerer det forventede output.

I et faktisk scenarie bør vi oprette flere enhedstests for at verificere adfærden af ​​vores algoritme med andre mulige input.

4. Anvendelse af regulære udtryk

Implementering af en parser er en effektiv tilgang. Den resulterende algoritme er dog relativt stor og kompleks. Som et alternativ kan vi altså bruge regulære udtryk.

Dernæst vil vi diskutere to mulige implementeringer, der er afhængige af regulære udtryk. Ikke desto mindre bør de bruges med forsigtighed, da deres behandlingstid er høj sammenlignet med den tidligere fremgangsmåde. Derfor kan brug af regulære udtryk til dette scenarie være uoverkommeligt ved behandling af store mængder inputdata.

4.1. String split() Metode

I denne første regulære udtryksmulighed bruger vi split() metode fra strengen klasse. Denne metode opdeler strengen omkring matcher af det givne regulære udtryk:

String[] tokens = input.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);

Ved første øjekast kan det regulære udtryk virke meget komplekst. Dens funktionalitet er dog relativt enkel.

Kort sagt fortæller ved at bruge positivt lookahead kun at dele rundt om et komma, hvis der ikke er dobbelte anførselstegn, eller hvis der er et lige antal dobbelte anførselstegn foran det.

Den sidste parameter i split() metoden er grænsen. Når vi angiver en negativ grænse, anvendes mønsteret så mange gange som muligt, og den resulterende række af tokens kan have en hvilken som helst længde.

4.2. Guavas Splitter Klasse

Et andet alternativ baseret på regulære udtryk er brugen af ​​Splitteren klasse fra Guava-biblioteket:

Pattern pattern = Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
Splitter splitter = Splitter.on(pattern);
List<String> tokens = splitter.splitToList(input);

Her opretter vi en splitter objekt baseret på det samme regulære udtryksmønster som før. Efter oprettelse af splitter , bruger vi splitToList() metode, som returnerer en Liste af tokens efter opdeling af input String .

5. Brug af et CSV-bibliotek

Selvom de præsenterede alternativer er interessante, kan det være nødvendigt at bruge et CSV-parsingbibliotek såsom OpenCSV.

Brug af et CSV-bibliotek har den fordel, at det kræver mindre indsats, da vi ikke behøver at skrive en parser eller et komplekst regulært udtryk. Som et resultat ender vores kode med at være mindre fejltilbøjelig og lettere at vedligeholde.

Desuden kan et CSV-bibliotek være den bedste tilgang, når vi ikke er sikre på formen på vores input . For eksempel kan inputtet have undslupne anførselstegn, som ikke ville blive håndteret korrekt af tidligere tilgange.

For at bruge OpenCSV skal vi inkludere det som en afhængighed. I et Maven-projekt inkluderer vi opencsv-afhængigheden:

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>4.1</version>
</dependency>

Derefter kan vi bruge OpenCSV som følger:

CSVParser parser = new CSVParserBuilder()
  .withSeparator(',')
  .build();

CSVReader reader = new CSVReaderBuilder(new StringReader(input))
  .withCSVParser(parser)
  .build();

List<String[]> lines = new ArrayList<>();
lines = reader.readAll();
reader.close();

Brug af CSVParserBuilder klasse, starter vi med at lave en parser med en kommaseparator. Derefter bruger vi CSVReaderBuilder at oprette en CSV-læser baseret på vores komma-baserede parser.

I vores eksempel leverer vi en StringReader som et argument til CSVReaderBuilder konstruktør. Vi kan dog bruge forskellige læsere (f.eks. en fillæser), hvis det kræves.

Til sidst kalder vi readAll() metode fra vores læser objekt for at få en liste af streng arrays. Da OpenCSV er designet til at håndtere multi-line input, hver position i linjerne liste svarer til en linje fra vores input. For hver linje har vi således en streng array med de tilsvarende kommaseparerede værdier.

I modsætning til tidligere tilgange fjernes dobbelte anførselstegn med OpenCSV fra det genererede output.

6. Konklusion

I denne artikel undersøgte vi flere alternativer til at ignorere kommaer i anførselstegn ved opdeling af en kommasepareret streng . Udover at lære at implementere vores egen parser, udforskede vi brugen af ​​regulære udtryk og OpenCSV-biblioteket.

Som altid er kodeeksemplerne brugt i denne tutorial tilgængelige på GitHub.


Java tag