- PR -

文字列の解析について

投稿者投稿内容
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2007-11-25 15:39
引用:

unibonさんの書き込み (2007-11-19 14:07) より:
\ の前置ならば、パースも、
(1) \ の対処
(2) = と , の対処
を独立してできるので処理も楽です。


と、書いたのですが、良く考えたら(1)と(2)を独立させることはできませんでした。
なにも使わずに0からコードを書いてみましたが、長い。およそ60行。コーディングに2時間くらいかかりました。でも、私としては正規表現よりはこっちのほうを選択したいです。
コード:
import java.util.HashMap;
import java.util.Map;

public class MyParser {

    private boolean left;
    private String key;
    private String value;
    private Map<String, String> map;

    private void push(char c) {
        if (left) {
            key += c;
        } else {
            value += c;
        }
    }

    private void save() {
        if (left) {
            left = false;
        } else {
            throw new IllegalStateException();
        }
    }
    
    private void commit() {
        if (left) {
            throw new IllegalStateException();
        } else {
            map.put(key.trim(), value.trim());
            key = "";
            value = "";
            left = true;
        }
    }
    
    public Map<String, String> parse(String s, char escChar) {
        left = true;
        key = "";
        value = "";
        map = new HashMap<String, String>();
        boolean esc = false;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (esc) {
                push(c);
                esc = false;
            } else {
                if (c == escChar) {
                    esc = true;
                } else if (c == '=') {
                    save();
                } else if (c == ',') {
                    commit();
                } else {
                    push(c);
                }
            }
        }
        commit();
        return map;
    }
    
    public static void main(String[] args) {
        String s = "A1=aaa,B1=bbb,C\\=1=c\\,cc,D\\\\1=ddd,E1\\\\=eee\\\\,F1=fff";
        MyParser p = new MyParser();
        Map<String, String> map = p.parse(s, '\\');
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println("key = " + key + ", value = " + value);
        }
    }
}



--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}
84
ベテラン
会議室デビュー日: 2005/11/04
投稿数: 83
投稿日時: 2007-11-28 14:26
>かつのりさん
確かにパフォーマンスで決めるというのも手ですね。
パフォーマンスがさほどかわらなければ、
保守性を考えて、
よみやすく、わかりやすい方にしようと思います。

>rancoさん
一般解でないにしても、参考になるコードありがとうございます。
やり方次第でこんなに短く書けるのですね。
驚きです。

>unibonさん
自分で書いたコードよりもわかりやすいので、
とても参考になります。

このような投稿に
2時間も費やしていただいて申し訳ないかぎりです。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2007-11-28 14:40
どうでも良いことですが、忘れそうなので書いておきますと、
引用:

unibonさんの書き込み (2007-11-25 15:39) より:
コード:
        Map<String, String> map = p.parse(s, '\\');




は、
コード:
        Map<String, String> map = p.parse(s, '\\\');


です。
(掲示板の制約で、シングルクォーテーションで \ マークを囲むと、\ マークの表示が1個減ってしまいますので。なぜなんでしょう?プレビューするかしないかでも違うみたいです。)

--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}
hei
ベテラン
会議室デビュー日: 2006/09/07
投稿数: 78
投稿日時: 2007-12-02 15:17
こんにちは。
すでに解決済みなようですが面白そうだったのでやってみました。

rancoさんの
引用:


エスケープされたバックスラッシュが複数個連続していると、エスケープされている,=を判別できない場合があります。



も解決していると思います。
(違ってたらすみません)

コード:

public static java.util.Map parse(String text){
Map map=new java.util.HashMap();
Pattern outerP=Pattern.compile("[^\\\\,]*(?:\\\\.[^\\\\,]*)*");
Pattern innerP=Pattern.compile("([^\\\\=]*(?:\\\\.[^\\\\=]*)*)=([^\\\\=]*(?:\\\\.[^\\\\=]*)*)");
Matcher outerM=outerP.matcher(text);
while(outerM.find()){
Matcher innerM=innerP.matcher(outerM.group());
if(innerM.find()){
map.put(innerM.group(1),innerM.group(2));
}
}
return map;
}


書籍「詳細 正規表現 第2版」を参考にしています。



[ メッセージ編集済み 編集者: hei 編集日時 2007-12-02 15:24 ]
ranco
大ベテラン
会議室デビュー日: 2007/11/02
投稿数: 112
投稿日時: 2007-12-02 18:59
そうなんですよ。String.split()にできないことが、Pattern/Matcherを使えばできる場合は、けっこう多いですね。不特定のネスティング(入れ子状)は、後者でも難物ですが。

スキルアップ/キャリアアップ(JOB@IT)