おそらく〜100のエントリを含む次の文字列があります:
String foo = "{k1=v1,k2=v2,...}"
そして、次の関数を書きたいと思っています:
String getValue(String key){
// return the value associated with this key
}
解析ライブラリを使用せずにこれを行いたいと思います。迅速な何かのアイデアはありますか?
おそらく〜100のエントリを含む次の文字列があります:
String foo = "{k1=v1,k2=v2,...}"
そして、次の関数を書きたいと思っています:
String getValue(String key){
// return the value associated with this key
}
解析ライブラリを使用せずにこれを行いたいと思います。迅速な何かのアイデアはありますか?
文字列が常に次のようになることがわかっている場合は、次のようにしてみてください。
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();
}
警告: 実際には試していません。マイナーな構文エラーがあるかもしれませんが、ロジックは健全なはずです。また、エラー チェックをまったく行わなかったことに注意してください。
私が考えることができる最も高速ですが、最も醜い答えは、ステートマシンを使用して文字ごとに解析することです。非常に高速ですが、非常に具体的で非常に複雑です。私の見方では、いくつかの状態を持つことができます:
例:
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;
}
}
さらに、StringTokenizers (高速) または Regexs (低速) を使用すると、これをはるかに高速に実装できます。しかし、全体としては、個々の文字の解析がおそらく最速の方法です。
文字列に多くのエントリがある場合は、メモリを節約するために StringTokenizer を使用せずに手動で解析する方がよい場合があります (これらの文字列を何千も解析する必要がある場合は、追加のコードを使用する価値があります)。
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;
}
これらの文字列を使用してこのコードをテストしたところ、正常に動作しました。
{k1=v1}
{k1=v1、k2 = v2、k3= v3、k4 =v4}
{k1= v1,}
key
inの存在をチェックするコードを追加するfoo
ことは、読者の練習問題として残されています:-)
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));
}
テストなしで書かれています:
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;
はい、それは醜いです:-)
私の解決策を見つけてください:
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);
}
}
使用法
KeyValueParser line = new KeyValueParser("user_uid=224620; pass=e10adc3949ba59abbe56e057f20f883e;", ";", "=");
String userUID = line.getValue("user_uid")
さて、値に「=」も「,」もないと仮定すると、最も単純な(そして粗末な)方法は次のとおりです。
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;
ええ、お勧めしません:)