質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.35%

  • Java

    14384questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

数値を含む文字列のソート

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 1,937

oscikonome

score 9

「"任意の文字列" + 数値」という文字列が複数あり、これを昇順に並べ替えたいです。
文字列ソートだと100が2より前に来てしまうため、正規表現とComparatorで実装しようとしました。

しかし、文字列の重複があり得ることになり(例えばABC1が複数出現する)、その場合は連番を付与することになりました。(連番のつけ方については見て連番だとわかればよいのですが、ABC1、ABC1_2、ABC1_3・・・が基本方針となりました)
単純に末尾から数値を取得して比較すればよいと思っていたのが、連番が付与されることになって考え直すことになり、悩んでいます。

例:
ABC1、ABC1_2、ABC2、ABC2_2、ABC10、ABC011と並べたいです。

よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • cateye

    2017/04/03 11:26

    "任意の文字列"は文字列長は決まっていますか? (アルファベットのみとした場合)大文字・小文字の区別は?

    キャンセル

  • oscikonome

    2017/04/03 11:29

    長さは決まっていません。また、大小の区別はしたいです。

    キャンセル

回答 3

checkベストアンサー

+2

こういうところを使っていろいろ試してみましょう。→Regular Expression Test Drive
まず、正規表現のパターン文字列は次のようになります。

".+?(\d+)(?:_(\d+))?"


最初の".+"はおなじみ「任意の文字列」ですが、今回は".+?"としています。理由は後述。
そのあとの"(\d+)"も「1文字以上の数字」でおなじみ。グループ化してインデックスは1番になります。
そのあと、"(?:_(\d+))?"としています。"(?:...)"は「グループ化だけ行う括弧」で、グループ化はするが、groupメソッドで使うインデックスにカウントされない性質を持ちます。ここでは"_(\d+)"(アンダースコアと数字1文字以上のつながり)をグループとして、その次の"?"(直前が0回か1回を意味する量指定子)の対象にしています。この数字の部分がグループ2になります。

さて、最初の任意の文字列の部分で".+?"とした理由についてですが、最後のグループが関係します。
最後のグループの量指定子を"?"にしたため、最後のグループについては「無くてもいい」扱いになります。
最初を".+"とした場合、「パターンにマッチすることを優先に最長部分とマッチ」するため、最後のグループをないことにし、末尾の数字をグループ1の数字とみなして、それ以外を".+"にマッチ、という形になり、連番に対応できなくなってしまいます。
そこで、パターンにマッチするよう「最短部分」にマッチさせるため、".+?"としています。

長くなりましたが、これを使ってComparatorを作成すると(文字列がこのパターンにマッチすること前提)
こんな感じになるのでは(未検証)

Comparator<String> comparator = new Comparator<>() {
    Pattern pattern = Pattern.compile(".+?(\\d+)(?:_(\\d+))?");
    @Override
    public int compare(String s1, String s2) {
        Matcher m1 = pattern.matcher(s1);
        Matcher m2 = pattern.matcher(s2);
        if (m1.find() && m2.find()) {
            int n1 = Integer.parseInt(m1.group(1));
            int n2 = Integer.parseInt(m2.group(1));
            int x = Integer.compare(n1, n2);
            if (x == 0) {
                n1 = m1.group(2).equals("") ? 1 : Integer.parseInt(m1.group(2));
                n2 = m2.group(2).equals("") ? 1 : Integer.parseInt(m2.group(2));
                x = Integer.compare(n1, n2);
            }
            return x;
        } else {
            return 0; //どちらかがマッチしなければこの方法での比較はできないので、場合によっては例外スロー
        }
    }
};

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

数値部分を数字文字列にした上で、数値の最大値に合わせてゼロで桁合わせしてはどうでしょうか。
例えば数値の最大値が999ならば1~9を "001"~"009"、10から99を"010"~"099"のようにしてソートする。
いかがでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/03 11:18

    「これはいい」と思ってプラスしたんですが、ゼロパディングの手順を考えると、わざわざ数字に直して文字列に直して連結するようになりますし、比較も数値同士の比較の方が効率的なので、申し訳ないけど取り消しました。

    キャンセル

  • 2017/04/03 11:22 編集

    ありがとうございます。
    最大桁は指定されていませんが、おそらく3桁もあれば十分だと思います。
    ABC1_2をABC001_002と変換したうえで比較するということですね、試してみようと思います。

    キャンセル

  • 2017/04/03 11:25

    すみません。単純な加算ではダメだったので、こちらの方が正しくできます。訂正して戻します。

    キャンセル

+1

単純に、「数字1_数字2」なら数字1に数字2を足して1引いたものを値とし、「_」がなければ末尾の数字を値とすればいいのでは?

追記

単純な加算だと 2_3 が 3 より大きくなる問題がありましたので訂正します。
数字1に重みをつけてから加算しなければなりませんでした。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/03 11:11 編集

    正規表現だと ([0-9]+)(?:_([0-9]+))?$ で $1 と $2 にあたります。

    キャンセル

  • 2017/04/03 11:19

    ありがとうございます。
    「ABC_2_2」と「ABC_1_3」の場合のように、文字列部分に「_」があり得るのと、数字1+数字2が同じ時があり得るのが心配ですが、参考にさせていただきます。

    キャンセル

  • 2017/04/03 11:24 編集

    その例であれば数字の取得に関しては問題ありませんが、単純に足した場合、2_3 が 3 より大きくなる問題がありますので、少数にする必要がありました。すみません。

    キャンセル

  • 2017/04/03 11:32

    2_2の部分を2.2、1_3を1.3として比較するわけですね。ABC_1と連番がないものも1.0にすると。

    キャンセル

  • 2017/04/03 11:38

    そうですね。
    または数字1に例えば1000などの定数を掛ける方がいいかもしれません。

    キャンセル

  • 2017/04/03 13:59

    数字2が2桁になるとまた問題では?

    キャンセル

  • 2017/04/03 15:52

    そうですね。小数の場合は 0.1 と 0.10 が同じ数になるので、文字列としてつないだのではよくないと思います。
    数字2を数値に直して、例えば 1000 で割るなどすればうまくいくのではないかと。数字1を大きくした方が効率は良さそうです。

    キャンセル

同じタグがついた質問を見る

  • Java

    14384questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。