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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Java

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

Q&A

解決済

3回答

9177閲覧

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

oscikonome

総合スコア16

Java

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

0グッド

0クリップ

投稿2017/04/03 02:00

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

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

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

よろしくお願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cateye

2017/04/03 02:26

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

2017/04/03 02:29

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

回答3

0

ベストアンサー

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

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

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

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

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

java

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

投稿2017/04/03 14:04

編集2017/04/03 14:05
swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

投稿2017/04/03 02:09

tkanda

総合スコア2425

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2017/04/03 02:18

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

2017/04/03 02:25 編集

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

2017/04/03 02:25

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

0

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

追記

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

投稿2017/04/03 02:05

編集2017/04/03 02:28
Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2017/04/03 02:11 編集

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

2017/04/03 02:19

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

2017/04/03 02:28 編集

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

2017/04/03 02:32

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

2017/04/03 02:38

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

2017/04/03 04:59

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

2017/04/03 06:52

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問