Análisis de cadenas de Java: {k1 = v1, k2 = v2, & # 8230;}
-
10-07-2019 - |
Pregunta
Tengo la siguiente cadena que probablemente contendrá ~ 100 entradas:
String foo = "{k1=v1,k2=v2,...}"
y estoy buscando 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?
Solución
Si sabe que su cadena siempre se verá así, intente 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: en realidad no intenté esto; Puede haber pequeños errores de sintaxis, pero la lógica debe ser sólida. Tenga en cuenta que también hice exactamente cero verificación de errores, por lo que es posible que desee hacer lo que hice más robusto.
Otros consejos
La respuesta más rápida, pero más fea que se me ocurre, es analizarla carácter por carácter utilizando una máquina de estados. Es muy rápido, pero muy específico y bastante complejo. A mi modo de ver, podría tener varios estados:
- Clave de análisis
- Valor de análisis
- 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 mucho más rápido utilizando StringTokenizers (que son rápidos) o Regexs (que son más lentos). Pero, en general, el análisis de caracteres individuales es probablemente la forma más rápida.
Si la cadena tiene muchas entradas, es mejor que analice manualmente sin un StringTokenizer para guardar algo de memoria (en caso de que tenga 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 funciona bien:
{k1 = v1}
{k1 = v1, k2 = v2, k3 = v3, k4 = v4}
{k1 = v1,}
Escrito sin prueba:
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 :-)
Bueno, suponiendo que no '=' ni ',' en valores, el método más simple (y lamentable) 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 recomendado :)
Agregar código para verificar la existencia de key
en food
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));
}
Encuentra 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")