2009-10-29 4 views
8

ho la seguente stringa che probabilmente conterrà ~ 100 voci:Java String analisi - {k1 = v1, v2 = k2, ...}

String foo = "{k1=v1,k2=v2,...}" 

e sto cercando di scrivere la seguente funzione:

String getValue(String key){ 
    // return the value associated with this key 
} 

Mi piacerebbe farlo senza utilizzare alcuna libreria di analisi. Qualche idea per qualcosa di veloce?

+0

Biblioteca parsing vuoi dire regex, o nessuna terza parte biblioteca? – Yishai

+0

Can v1, v2 ... contengono '=' o ','? – sinuhepop

+0

Supponiamo che i valori non contengano '=' o ','. Solo no libs di terze parti. – tinkertime

risposta

12

Se si conosce la stringa sarà sempre simile a questa, provare qualcosa di simile:

HashMap map = new HashMap(); 

public void parse(String foo) { 
    String foo2 = foo.substring(1, foo.length() - 1); // hack off braces 
    StringTokenizer st = new StringTokenizer(foo2, ","); 
    while (st.hasMoreTokens()) { 
    String thisToken = st.nextToken(); 
    StringTokenizer st2 = new StringTokenizer(thisToken, "="); 

    map.put(st2.nextToken(), st2.nextToken()); 
    } 
} 

String getValue(String key) { 
    return map.get(key).toString(); 
} 

Attenzione: non ho fatto provare questo; potrebbero esserci errori di sintassi minori, ma la logica dovrebbe essere corretta. Nota che ho anche fatto esattamente il controllo degli errori a zero, quindi potresti voler rendere più robusto quello che ho fatto.

+1

Un collegamento userebbe '", = {} "'. Nessun hacking off parentesi o un secondo tokenizer necessario :) – rsp

+0

@rsp: buon punto! – Tenner

4

La risposta più rapida, ma più brutta che riesca a pensare, è analizzarla carattere per carattere usando una macchina a stati. È molto veloce, ma molto specifico e piuttosto complesso. Il mio modo di vedere, si potrebbe avere diversi stati:

  • Analisi chiave
  • nell'esame del valore
  • Pronto

Esempio:

int length = foo.length(); 
int state = READY; 
for (int i=0; i<length; ++i) { 
    switch (state) { 
     case READY: 
     //Skip commas and brackets 
     //Transition to the KEY state if you find a letter 
     break; 
     case KEY: 
     //Read until you hit a = then transition to the value state 
     //append each letter to a StringBuilder and track the name 
     //Store the name when you transition to the value state 
     break; 
     case VALUE: 
     //Read until you hit a , then transition to the ready state 
     //Remember to save the built-key and built-value somewhere 
     break; 
    } 
} 

Inoltre, è possibile implementare questo molto più veloce usando StringTokenizers (che sono veloci) o Regexs (che sono più lenti). Ma nel complesso, l'analisi individuale dei personaggi è probabilmente il modo più veloce.

+0

Per la velocità raw, utilizzare il char array per evitare la sincronizzazione. Bene, questo è un riflesso dei vecchi tempi dato che le moderne JVM forzano le serrature :-) – cadrian

+0

Oh, bella chiamata. In realtà ho completamente dimenticato di inserire come effettivamente accedere ai personaggi ... – Malaxeur

0

scritto senza prove:

String result = null; 
int i = foo.indexOf(key+"="); 
if (i != -1 && (foo.charAt(i-1) == '{' || foo.charAt(i-1) == ',')) { 
    int j = foo.indexOf(',', i); 
    if (j == -1) j = foo.length() - 1; 
    result = foo.substring(i+key.length()+1, j); 
} 
return result; 

Sì, è brutto :-)

0

Beh, assumendo nessun '=' né '' nei valori, il metodo più semplice (e squallido) è:

int start = foo.indexOf(key+'=') + key.length() + 1; 
int end = foo.indexOf(',',i) - 1; 
if (end==-1) end = foo.indexOf('}',i) - 1; 
return (start<end)?foo.substring(start,end):null; 

Sì, non è raccomandato :)

+0

Non credo che userò questo, ma una risposta interessante! – tinkertime

+0

Oh, lo so non è il modo buono :) Volevo solo indicare che questo è un metodo veloce. Ma alcuni utenti sono più veloci di me e hanno pubblicato soluzioni simili prima. Non vedo buone soluzioni anche nelle altre risposte, e la soluzione finale implicherebbe l'utilizzo di un parser AST o qualcosa di simile. – sinuhepop

2

Se la stringa ha molte voci che potrebbe essere meglio di parsing manualmente senza uno StringTokenizer per risparmiare un po 'di memoria (nel caso in cui si deve analizzare migliaia di queste stringhe, vale la pena il codice aggiuntivo):


public static Map parse(String s) { 
    HashMap map = new HashMap(); 
    s = s.substring(1, s.length() - 1).trim(); //get rid of the brackets 
    int kpos = 0; //the starting position of the key 
    int eqpos = s.indexOf('='); //the position of the key/value separator 
    boolean more = eqpos > 0; 
    while (more) { 
     int cmpos = s.indexOf(',', eqpos + 1); //position of the entry separator 
     String key = s.substring(kpos, eqpos).trim(); 
     if (cmpos > 0) { 
      map.put(key, s.substring(eqpos + 1, cmpos).trim()); 
      eqpos = s.indexOf('=', cmpos + 1); 
      more = eqpos > 0; 
      if (more) { 
       kpos = cmpos + 1; 
      } 
     } else { 
      map.put(key, s.substring(eqpos + 1).trim()); 
      more = false; 
     } 
    } 
    return map; 
} 

ho provato questo codice con queste stringhe e funziona benissimo:

{k1 = v1}

{k1 = v1, v2 = k2, k3 = v3, v4 k4 =}

{k1 = v1,}

0

Aggiunta di codice per controllare esistenza di key in foo viene lasciato come esercizio al lettore :-)

String foo = "{k1=v1,k2=v2,...}"; 

String getValue(String key){ 
    int offset = foo.indexOf(key+'=') + key.length() + 1; 
    return foo.substring(foo.indexOf('=', offset)+1,foo.indexOf(',', offset)); 
} 
0

Si prega di trovare la mia soluzione:

public class KeyValueParser { 

    private final String line; 
    private final String divToken; 
    private final String eqToken; 
    private Map<String, String> map = new HashMap<String, String>(); 

    // user_uid=224620; pass=e10adc3949ba59abbe56e057f20f883e; 
    public KeyValueParser(String line, String divToken, String eqToken) { 
     this.line = line; 
     this.divToken = divToken; 
     this.eqToken = eqToken; 
     proccess(); 
    } 

    public void proccess() { 
     if (Strings.isNullOrEmpty(line) || Strings.isNullOrEmpty(divToken) || Strings.isNullOrEmpty(eqToken)) { 
      return; 
     } 
     for (String div : line.split(divToken)) { 
      if (Strings.isNullOrEmpty(div)) { 
       continue; 
      } 
      String[] split = div.split(eqToken); 
      if (split.length != 2) { 
       continue; 
      } 
      String key = split[0]; 
      String value = split[1]; 
      if (Strings.isNullOrEmpty(key)) { 
       continue; 
      } 
      map.put(key.trim(), value.trim()); 
     } 

    } 

    public String getValue(String key) { 
     return map.get(key); 
    } 
} 

Uso

KeyValueParser line = new KeyValueParser("user_uid=224620; pass=e10adc3949ba59abbe56e057f20f883e;", ";", "="); 
String userUID = line.getValue("user_uid")