2009-10-29 10 views
8

tengo la siguiente cadena que probablemente contendrá ~ 100 entradas:String de Java análisis - {k1 = v1, v2 = k2, ...}

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

y estoy buscando para escribir la siguiente función:

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

Me gustaría hacer esto sin usar ninguna biblioteca de análisis. ¿Alguna idea para algo rápido?

+0

Mi biblioteca de análisis sintáctico que quiere decir la expresión regular, o ningún tercero ¿biblioteca? – Yishai

+0

¿Puede v1, v2 ... contener '=' o ','? – sinuhepop

+0

Supongamos que los valores no contienen '=' o ','. Simplemente no hay libs de terceros. – tinkertime

Respuesta

12

Si sabe que su cadena siempre se verá así, intentar algo como:

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

Advertencia: Yo en realidad no intento esto; puede haber errores de sintaxis menores, pero la lógica debe ser sólida. Tenga en cuenta que también hice exactamente cero comprobación de errores, por lo que es posible que desee hacer lo que hice más robusto.

+1

Un atajo estaría usando '", = {} "'. No hackear llaves o un segundo tokenizer necesario :) – rsp

+0

@rsp: ¡Buen punto! – Tenner

4

La respuesta más rápida, pero la más fea que puedo pensar es analizarla carácter por carácter usando una máquina de estado. Es muy rápido, pero muy específico y bastante complejo. La forma en que lo veo, usted podría tener varios estados:

  • de análisis clave
  • identificando el valor del
  • Listo

Ejemplo:

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

Además, puede implementar esto es mucho más rápido utilizando StringTokenizers (que son rápidos) o Regex (que son más lentos). Pero en general, el análisis de caracteres individuales es probablemente la forma más rápida.

+0

Para la velocidad bruta, use la matriz de caracteres para evitar la sincronización. Bueno, eso es un reflejo antiguo porque las JVM modernas engrosan las cerraduras :-) – cadrian

+0

Oh, buena llamada. De hecho, me olvidé por completo de ver cómo acceder a los personajes ... – Malaxeur

0

escrito sin pruebas:

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í, es feo :-)

0

Bueno, suponiendo que no hay '=' ni '', en los valores, el método más simple (y en mal estado) es:

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í, no es recomendable :)

+0

¡No creo que use esta, pero es una respuesta interesante! – tinkertime

+0

Oh, sé que no es la buena manera :) Solo quería indicar que este es un método rápido. Pero algunos usuarios son más rápidos que yo y publicaron soluciones similares antes. Tampoco veo buenas soluciones en las otras respuestas, y la solución final implicaría usar un analizador AST o algo similar. – sinuhepop

2

Si la cadena tiene muchas entradas que puede ser mejor análisis sintáctico manualmente sin StringTokenizer para ahorrar algo de memoria (en caso de tener que analizar miles de estas cadenas, vale la pena el código adicional):


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

Probé este código con estas cadenas y trabaja muy bien:

{k1 = v1}

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

{k1 = v1,}

0

Adición de código para comprobar existencia de key en foo se deja como ejercicio para el lector :-)

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

favor encuentre mi solución:

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") 
Cuestiones relacionadas