2012-01-15 1 views
7

Ricevo la risposta da un servizio come di seguito. Come analizzare questo in un Map? Ho inizialmente pensato di dividere in spazi vuoti ma non funziona in quanto il valore potrebbe contenere spazi, ad es. guarda il valore della chiave SA nella risposta seguente.C'è un modo semplice per analizzare questo testo in una mappa

Un'opzione che ho pensato è quella di dividere negli spazi fornito il carattere precedente è una doppia citazione. Non sono sicuro di come scrivere la regex per questo però.

TX = "0000000000108000001830001" FI = "" OS = "8" CI = "QU01SF1S2032" AW = "SSS" SA = "1525 Windward Concourse"

risposta

4

Parse a citazioni. Si potrebbe anche usare un'espressione regolare per trovare ogni coppia chiave/valore, assumendo che ogni valore sia tra virgolette. La mia unica domanda sarebbe, quali sono le regole per se un valore contiene virgolette incorporate? (Sono fuggiti con '\' o tali Indipendentemente da ciò, questo non è attualmente rappresentato in seguito ...?)

Ad esempio:

(\w+)="([^"]*)" 

Questo sarà anche darvi gruppi di # 1 e # 2 che può essere usato per fornire rispettivamente la chiave e il valore.

Eseguire questo in un ciclo, utilizzando il metodo Java Matcher.find(), fino a trovare tutte le coppie.

codice di esempio:

String input = "TX=\"0000000000108000001830001\" FI=\"\" OS=\"8\" CI=\"QU01SF1S2032\" AW=\"SSS\" SA=\"1525 Windward Concourse\""; 

Pattern p = Pattern.compile("\\s*(\\w+)=\"([^\"]*)\"\\s*"); 

Matcher m = p.matcher(input); 
while(m.find()){ 
    System.out.println(m.group(1)); 
    System.out.println(m.group(2)); 
} 

uscita:

TX 
0000000000108000001830001 
FI 

OS 
8 
CI 
QU01SF1S2032 
AW 
SSS 
SA 
1525 Windward Concourse 
+2

Accidenti, basta usare le virgolette singole; è etichettato Groovy :) –

+0

@DaveNewton - Lo lasceremo come esercizio per l'OP. :-) – ziesemer

+0

@ziesemer - +1. Ma sto ottenendo il valore dopo "=" stampato con doppio quoes come "0000000000108000001830001" –

2

StreamTokenizer è veloce, anche se non ho usato la funzione quoteChar(). Esempi possono essere trovati here, here e here.

Console:

 
TX=0000000000108000001830001 
FI= 
OS=8 
CI=QU01SF1S2032 
AW=SSS 
SA=1525 Windward Concourse 
Count: 6 
0.623 ms 

Codice:

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.StreamTokenizer; 
import java.io.StringReader; 

/** @see https://stackoverflow.com/questions/8867325 */ 
public class TokenizerTest { 

    private static final String s = "" 
     + "TX=\"0000000000108000001830001\" FI=\"\" OS=\"8\" " 
     + "CI=\"QU01SF1S2032\" AW=\"SSS\" SA=\"1525 Windward Concourse\""; 
    private static final char equal = '='; 
    private static final char quote = '"'; 
    private static StreamTokenizer tokens = new StreamTokenizer(
     new BufferedReader(new StringReader(s))); 

    public static void main(String[] args) { 
     long start = System.nanoTime(); 
     tokenize(); 
     long stop = System.nanoTime(); 
     System.out.println((stop - start)/1000000d + " ms"); 
    } 

    private static void tokenize() { 
     tokens.ordinaryChar(equal); 
     tokens.quoteChar(quote); 
     try { 
      int count = 0; 
      int token = tokens.nextToken(); 
      while (token != StreamTokenizer.TT_EOF) { 
       if (token == StreamTokenizer.TT_WORD) { 
        System.out.print(tokens.sval); 
        count++; 
       } 
       if (token == equal) { 
        System.out.print(equal); 
       } 
       if (token == quote) { 
        System.out.println(tokens.sval); 
       } 
       token = tokens.nextToken(); 
      } 
      System.out.println("Count: " + count); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 
+0

Buono a sapersi su StreamTokenizer –

+0

Ho solo _had_ per provare il 'quoteChar()' ; più sopra. – trashgod

+0

Penso che questa soluzione sia eccessivamente complicata. A meno che non ci sia un grande vincolo di prestazioni, consiglierei di andare con una soluzione più semplice, come usare una regex (e se la performance è un vincolo, dovrebbe essere profilata per vedere se è molto più veloce di una regex, cosa di cui dubito). – epidemian

3

A quanto pare il testo sembra che potrebbe essere un XML. È così, o è quel testo la risposta grezza del servizio? Se si tratta di un XML è possibile analizzare facilmente con XmlSlurper Groovy:

def input = '<root TX="0000000000108000001830001" FI="" OS="8" CI="QU01SF1S2032" AW="SSS" SA="1525 Windward Concourse"></root>' 
def xml = new XmlSlurper().parseText(input) 

def map = xml.attributes() 

La variabile map sarebbe [CI:QU01SF1S2032, AW:SSS, TX:0000000000108000001830001, OS:8, FI:, SA:1525 Windward Concourse]

Se non è un XML, si può seguire ziesemer's answer e utilizzare un'espressione regolare. Una versione groovier della sua risposta che genera un Map sarebbe:

def input = 'TX="0000000000108000001830001" FI="" OS="8" CI="QU01SF1S2032" AW="SSS" SA="1525 Windward Concourse"' 
def match = input =~ /(\w+)="([^"]*)"/ 

def map = [:] 
match.each { 
    map[it[1]] = it[2] 
} 

Il risultato di map sarebbe la stessa di prima.

+0

Puoi anche fare: 'def map = (corrisponde come lista) .collectEntries {[(it [1]): it [2]]}' –

+0

@tim_yates Nice! Ho provato a chiamare 'collectEntries' sull'oggetto' match', ma non ha quel metodo, solo i metodi di iterazione standard. Non pensavo di convertirlo prima in un 'List'. A proposito, un 'inject' può anche fare il trucco = D – epidemian