2013-06 << 2013-07 >> 2013-08

2013-07-28 (日)

*数値の混ざったファイル名のソート

よくある処理なのだけどファイル名中の数字は数値として評価してソートしたい.

例えば,10.jpg は 9.jpgより後に来るべきだし,hoge_2012.3.1-012 は hoge_2012.12.4-123 より前になるはず.

なんか書いたことがある気はするけど,Javaで改めて書いてみる.もちろんJavaのComparatorの実装がどこかにあるのだろうけど,これだけのために依存ライブラリ増やしたくないし,コピペできそうなコードは残念な感じなのが多い.

最初手を抜いて,

Pattern numPtn = Pattern.compile("((?<=\\d)(?=[^\\d]))|((?<=[^\\d])(?=\\d))");
private int compare(final String s1, final String s2) {
    String ss1[] = numPtn.split(s1);
    String ss2[] = numPtn.split(s2);
    int n = ss1.length < ss2.length ? ss1.length : ss2.length;
    for (int i = 0; i < n; i++) {
        char c1 = ss1[i].charAt(0), c2 = ss2[i].charAt(0);
        if ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9')) {
            long d = Long.parseLong(ss1[i]) - Long.parseLong(ss2[i]);
            if (d != 0) return d > 0 ? 1 : -1;
        } else {
            int d = ss1[i].compareTo(ss2[i]);
            if (d != 0) return d;
        }
    }
    return ss1.length - ss2.length;
}

こう書いてたのだけど,遅くて使い物にならなかった.仕方ないので,

private int compare(final String s1, final String s2) {
    int l1 = s1.length(), l2 = s2.length();
    int p1 = 0, p2 = 0;
    while (p1 < l1 && p2 < l2) {
        char c1 = s1.charAt(p1++), c2 = s2.charAt(p2++);
        if ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9')) {
            long n1 = c1, n2 = c2;
            while (p1 < l1) {
                char c = s1.charAt(p1);
                if (c < '0' || c > '9') break;
                n1 = n1 *10 + c - '0';
                p1++;
            }
            while (p2 < l2) {
                char c = s2.charAt(p2);
                if (c < '0' || c > '9') break;
                n2 = n2 *10 + c - '0';
                p2++;
            }
            if (n1 != n2) return n1 > n2 ? 1 : -1;
        } else {
            if (c1 != c2) return c1 - c2;
        }
    }
    return (l1-p1) - (l2-p2);
}

とりあえずこれで.

2013-06 << 2013-07 >> 2013-08