Pergunta

Eu tenho a seguinte seqüência que provavelmente irá conter ~ 100 entradas:

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

e estou olhando para escrever a seguinte função:

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

Eu gostaria de fazer isso sem usar qualquer biblioteca de análise. Alguma idéia para algo rápido?

Foi útil?

Solução

Se você sabe sua corda vai sempre olhar como este, tente 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();
}

Atenção: Eu não realmente tentar isso; pode haver erros de sintaxe menores, mas a lógica deve ser sólida. Note que eu também fiz exatamente verificação de erros zero, de modo que você pode querer fazer o que eu fiz mais robusto.

Outras dicas

A mais rápida, mas a resposta mais feio que eu posso pensar é analisá-lo caractere por caractere usando uma máquina de estado. É muito rápido, mas muito específico e bastante complexa. A forma como eu vejo, você pode ter vários estados:

  • Análise de chave
  • Análise de Valor
  • Pronto

Exemplo:

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

Além disso, você pode implementar isso muito mais rápido usando StringTokenizers (que são rápidos) ou regexs (que são mais lento). Mas no geral, análise de caráter individual é mais provável que a maneira mais rápida.

Se o texto tem muitas entradas que você pode ser melhor fora de analisar manualmente sem um StringTokenizer para salvar um pouco de memória (no caso você tem que analisar milhares dessas cordas, vale a pena o código extra):


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

Eu testei este código com estas cordas e ele funciona muito bem:

{k1 = v1}

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

{k1 = v1,}

Escrito sem testar:

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;

Sim, é feio: -)

Bem, não assumindo '=' nem '', em valores, o método mais simples (e pobre) é:

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;

Sim, não recomendado:)

Como adicionar código para verificar existência de key em foo é deixada como exercício para o leitor: -)

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

Por favor, encontrar a minha solução:

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")
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top