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

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

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

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

Q&A

解決済

2回答

4359閲覧

replaceAllを1回にしたい

stakezaki

総合スコア46

Java

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

1グッド

1クリップ

投稿2017/02/19 08:38

以下の冗長なコードを同等の処理で高速になるように書きたいです。
replaceAllが6回呼ばれていますが、1回にすることはできますか?

Java

1 String str = "a\\\\r\\\\f\\\\b\\\\n\\\\t\\\\\"b"; 2 String replaced = str.replaceAll("\\\\r", "\r").replaceAll("\\\\f", "\f").replaceAll("\\\\b", "\b").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t").replaceAll("\\\\\"", "\""); 3 System.out.println(replaced);
KiyoshiMotoki👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

stackoverflow の以下の回答を参考にすると、
http://stackoverflow.com/a/7661573/6299234

以下のようになるでしょうか?

java

1String str = "a\\\\r\\\\f\\\\b\\\\n\\\\t\\\\\"b"; 2 3Map<String, String> replacements = new HashMap<String, String>() { 4 { 5 put("\\r", "\r"); 6 put("\\f", "\f"); 7 put("\\b", "\b"); 8 put("\\n", "\n"); 9 put("\\t", "\t"); 10 put("\\\"", "\""); 11 } 12}; 13 14String regexp = "\\\\r|\\\\f|\\\\r|\\\\b|\\\\n|\\\\t|\\\\\""; 15 16StringBuffer sb = new StringBuffer(); 17Pattern p = Pattern.compile(regexp); 18Matcher m = p.matcher(str); 19 20while (m.find()) { 21 m.appendReplacement(sb, replacements.get(m.group())); 22} 23 24m.appendTail(sb); 25 26System.out.println(sb.toString());

https://paiza.io/projects/EuCH4j-vlmh-3w7ygwm2tQ

一応、正規表現によるパターンマッチングは1回だけの実行となっていますが、
これで処理が高速になるかは不明です。

追記

今回のケースに限って言うと、replaceAll を replace に置き換えると高速化が期待できそうです。
https://paiza.io/projects/GjupaAu7dhuL51lKxitOzQ

replaceAll と異なり、replace は正規表現を使用しない文字列置換であるためです。
[http://docs.oracle.com/javase/jp/6/api/java/lang/String.html#replace(java.lang.CharSequence, java.lang.CharSequence)](http://docs.oracle.com/javase/jp/6/api/java/lang/String.html#replace(java.lang.CharSequence, java.lang.CharSequence))

投稿2017/02/20 06:12

編集2017/02/21 02:49
KiyoshiMotoki

総合スコア4791

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

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

KSwordOfHaste

2017/02/20 08:10

KiyoshiMotokiさんの回答で自分の回答の間違いに気づけました。回答ありがとうございました。
stakezaki

2017/02/20 11:44

ありがとうございます。こうするしかなさそうですね。
stakezaki

2017/02/21 02:14

ベンチマークとってみたら、以下のようになりました。 変換元文字列をランダムに生成したテキスト文字列220KBを処理 元のコード:47ms 上記コード:116ms
KiyoshiMotoki

2017/02/21 02:19

ご報告ありがとうございます。 案の定(w)、処理が複雑な分、遅くなってしまったようですね。 勉強させていただきました。
KiyoshiMotoki

2017/02/21 03:11 編集

一つ、思いついたことがあったので、回答欄に追記させていただきました。 ご確認ください。
KSwordOfHaste

2017/02/21 03:13 編集

自分もやってみましたがそこまでの差はありませんでした。220Kのランダムテキストを用いて、2つをそれぞれメソッドとして定義、あらかじめ数回ループさせてキャッシュヒット状態やJIT効果がそれなりに効くように配慮した後で10回測定し平均と標準偏差を出すと以下のようになりました。(単位ms)       平均 標準偏差 ------------------------------------- 元のコード:25.2 4.6 上記コード:25.9 5.7 なお、Patternはあらかじめコンパイルしておける利点がありますが、自分はそれをあえてせずに毎回Map生成/Pattern.compileをさせてます。
KiyoshiMotoki

2017/02/21 03:45

KSwordOfHaste様 検証ありがとうございます。 私の手元の環境では常に、速い順に  replace  Pattern による1回のマッチング  replaceAll となりましたが、Paiza.io で同じコードを繰り返し実行すると、 ときどき上の順番が入れ替わることがありました。 https://paiza.io/projects/AqtCaWjWlK5Dkx4ja_9tkg マシン性能や解析対象の文字列のサイズなどによっても、違いがあるのかも知れませんね。
KSwordOfHaste

2017/02/21 04:14

> マシン性能や解析対象の文字列のサイズなどによっても、違いがあるのかも そうかも知れませんね。ところでreplaceもやってみましたが・・・ replace  :33.4 9.2 上記コード:30.6 5.7 そもそも「上記コード」の平均値そのものが違ってます(汗)。同じ測定を複数回やるとreplace/上記コードが逆転する場合もありました。自分の測定も非常におおざっぱであったということでしょうねw;
stakezaki

2017/02/23 11:45

replaceAll()を使わないでベタに書いてみたところ非常に遅かったです。 450KBのランダムなテキストを実行してみたところ、 replaceAll:109 ms 以下のコード:136917 ms ``` private static String replace(String str) { String result = ""; int s = 0; while(true) { if (str.length()<=s) return result; char c = str.charAt(s); if (c!='\\') { result += c; s++; }else { switch(str.charAt(s+1)) { case '\\' : result += "\\"; s +=2; break; case 'r' : result += "\r"; s +=2; break; case 'f' : result += "\f"; s +=2; break; case 'b' : result += "\b"; s +=2; break; case 'n' : result += "\n"; s +=2; break; case 't' : result += "\t"; s +=2; break; case '"' : result += "\""; s +=2; break; } } } } ```
KiyoshiMotoki

2017/02/23 13:52

ご報告ありがとうございます。 このコードは、さすがに遅いはずです(笑) > 450KBのランダムなテキスト から str.charAt(s) で1文字ずつ取得しているため、  1 + 2 + ... + n (n はテキストの文字数) という回数だけ、テキストのシークが行なわれるからです。
KSwordOfHaste

2017/02/23 14:26

というよりString同士の連結が遅いのでは? StringBuilderを使うとかなり違うと思います。 先頭から1文字ずつ取り出すコード自体は悪くない気がします。
KiyoshiMotoki

2017/02/23 15:00 編集

KSwordOfHaste様 コメントありがとうございます。 確かに、 > String同士の連結 がパフォーマンスに与える影響の方が大きそうですね。 Java の場合、String型の連結は新たな Stringインスタンスを生成しますので。 ただし、 > 先頭から1文字ずつ取り出すコード自体は悪くない気がします。 につきましては、判断を保留させていただきますw
KSwordOfHaste

2017/02/24 04:08 編集

自分の意見を捕捉しておきますね~ > 先頭から1文字ずつ取り出すコード自体は悪くない気がします。 charAt(i)の処理時間はiの位置には依存せず常に固定です。またループは文字列の各要素アクセス回数は1回/要素なのでループ回数も最小限です。そのため「性能的には」悪くはないと思ったのでした。もちろん他の手段に比べて低水準な実装なので総合的にベストかと言われると?ではありますw
stakezaki

2017/02/26 05:15

ご指摘どおりStringが遅かったです。 ベタな書き方が一番はやい結果になりました。 https://paiza.io/projects/-G3yz0B02ZWjbVlVFxY-nA replaceOneTime=378ms replaceManyTime=243ms replaceNoRegex=42ms あと、replaceAllやreplaceだと、\\\\rを\\rに変換後、\rにまで変換してしまうバグがありました。replaceでやるなら、\\\\を一旦、別の値に置き換えて(私は\1\1に置換した)、最後に置き換えた値を\\に変換するということをやる必要がありました。(もっとよい回避策があるかもしれませんが) いずれにしても、ベタな書き方が一番はやかったので、こちらを採用しようと思っています。いろいろご意見くださり、ありがとうございました。
guest

0

訂正2:自分の回答は間違いです!
設問を勘違いしていました。\\rなどの3文字を1つの制御文字('\r'='\u000D'など)に置き換える問題なのですね。おわかりと思いますが、自分は'\'+'r'に置き換えるものと勘違いして回答してました。


String#replaceAllは正規表現が使えるので

String replaced = str.replaceAll("\\\\\\\\([rfbnt\\\\])", "\\\\$1");

でどうでしょう。質問の例でいえば結果は(エスケープなしに書くと)

a\r\f\b\n\t\\"b

になります。


訂正1:
コメントみて気づきました。ご質問のコードの最後のパターンは
\" -> "
なのですね。自分は
\ ->
と勘違いして回答してました。その点を訂正しておきます。

String replaced = str.replaceAll("\\\\\\\\([rfbnt\"])", "\\\\$1");

投稿2017/02/19 09:05

編集2017/02/20 08:09
KSwordOfHaste

総合スコア18392

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

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

stakezaki

2017/02/19 13:51

ありがとうございます。 ただ、結果が異なるようです。 もしかしたら、"\\\\$1"がエスケープ文字とみなされないのかもしれません。 ``` String str = "a\\\\r\\\\f\\\\b\\\\n\\\\t\\\\\"b"; String replaced = str.replaceAll("\\\\\\\\([rfbnt\\\\])", "\\\\$1"); String hexString = DatatypeConverter.printHexBinary(replaced.getBytes()); System.out.println(hexString); //615C725C665C625C6E5C745C5C2262 replaced= str.replaceAll("\\\\r", "\r").replaceAll("\\\\f", "\f").replaceAll("\\\\b", "\b").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t").replaceAll("\\\\\"", "\""); hexString = DatatypeConverter.printHexBinary(replaced.getBytes()); System.out.println(hexString); //615C0D5C0C5C085C0A5C095C2262 ```
swordone

2017/02/19 14:21

なんか当初の質問から余計な処理が入っていません?
KSwordOfHaste

2017/02/20 08:11

ようやく間違いに気づけました。かなり恥ずかしいです。失礼しました~
stakezaki

2017/02/20 11:43

大丈夫です。勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問