2009-10-29 8 views
8

Mam następujący ciąg znaków, który prawdopodobnie będzie zawierał ~ 100 wpisów:Java String parsowanie - {k1 = v1, v2 = k2, ...}

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

i szukam napisać następującą funkcję:

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

Chciałbym zrobić to bez użycia biblioteki parsującej. Jakieś pomysły na coś szybkiego?

+0

Moja parsowania biblioteki chodziło Ci regex, albo żadna osoba trzecia biblioteka? – Yishai

+0

Czy v1, v2 ... może zawierać "=" lub ","? – sinuhepop

+0

Załóżmy, że wartości nie zawierają "=" lub ",". Tylko bez bibliotek stron trzecich. – tinkertime

Odpowiedz

12

Jeśli znasz ciąg będzie zawsze wyglądać tak, spróbuj coś takiego:

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

Warning: Tak naprawdę nie spróbować; mogą występować drobne błędy składniowe, ale logika powinna brzmieć dobrze. Zauważ, że zrobiłem dokładnie zero sprawdzania błędów, więc możesz chcieć uczynić to, co zrobiłem bardziej niezawodne.

+1

Skrót będzie używał '", = {} "'. Nie trzeba łamać nawiasów klamrowych lub drugiego tokenizera :) – rsp

+0

@rsp: Dobra uwaga! – Tenner

4

Najszybszą, ale najbrzydszą odpowiedzią, jaką mogę wymyślić, jest przetwarzanie jej według postaci za pomocą automatu stanów. Jest bardzo szybki, ale bardzo szczegółowy i dość złożony. Tak jak ja to widzę, można mieć kilka stanów:

  • analizowaniem Kluczowe
  • analizowaniem wartość
  • Gotowy

Przykład:

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

Ponadto, można zaimplementować to znacznie szybciej za pomocą StringTokenizers (które są szybkie) lub Regexy (które są wolniejsze). Ale ogólnie, parsowanie poszczególnych postaci jest najprawdopodobniej najszybszą metodą.

+0

Aby uzyskać nieprzerobioną prędkość, użyj tablicy znaków, aby uniknąć synchronizacji. Cóż, to odruch starego czasu, odkąd nowoczesne maszyny JVM grasują zamki :-) – cadrian

+0

Dobra rozmowa. Właściwie zupełnie zapomniałem o tym, jak w rzeczywistości uzyskać dostęp do postaci ... – Malaxeur

0

Wpisany bez badań:

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; 

Tak, to brzydki :-)

0

Cóż, zakładając, że nie '=' ani '' w wartościach, najprostszy (i brudny) metoda jest:

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; 

Tak, nie polecam :)

+0

Nie myśl, że będę używał tej jednej, ale interesującej odpowiedzi! – tinkertime

+0

Oh, wiem, nie jest to dobra droga :) Chciałem tylko wskazać, że jest to szybka metoda. Ale niektórzy użytkownicy są szybsi ode mnie i zamieścili podobne rozwiązania wcześniej. Nie widzę też dobrych rozwiązań w innych odpowiedziach, a ostateczne rozwiązanie oznaczałoby użycie parsera AST lub czegoś podobnego. – sinuhepop

2

Jeżeli łańcuch ma wiele wpisów może być lepiej parsowanie ręcznie bez StringTokenizer zaoszczędzić trochę pamięci (w przypadku trzeba analizować tysiące tych strun, to jest warte dodatkowych code):


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

Testowałem tego kodu z tych ciągów i działa prawidłowo:

{K1 = V1}

{K1 = V1, k2 = V2 = V3 k3, k4 = v4}

{K1 = V1}

0

dodanie kodu w celu sprawdzenia istnienia w key w foo pozostaje jako ćwiczenie dla czytelnika :-)

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

Proszę znaleźć swoje rozwiązanie:

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

Wykorzystanie

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