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

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

ただいまの
回答率

90.33%

  • Java

    14412questions

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

replaceAllを1回にしたい

解決済

回答 2

投稿

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

stakezaki

score 32

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

        String str = "a\\\\r\\\\f\\\\b\\\\n\\\\t\\\\\"b";        
        String replaced = str.replaceAll("\\\\r", "\r").replaceAll("\\\\f", "\f").replaceAll("\\\\b", "\b").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t").replaceAll("\\\\\"", "\"");
        System.out.println(replaced);
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+3

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

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

String str = "a\\\\r\\\\f\\\\b\\\\n\\\\t\\\\\"b";

Map<String, String> replacements = new HashMap<String, String>() {
    {
        put("\\r", "\r");
        put("\\f", "\f");
        put("\\b", "\b");
        put("\\n", "\n");
        put("\\t", "\t");
        put("\\\"", "\"");
    }
};

String regexp = "\\\\r|\\\\f|\\\\r|\\\\b|\\\\n|\\\\t|\\\\\"";

StringBuffer sb = new StringBuffer();
Pattern p = Pattern.compile(regexp);
Matcher m = p.matcher(str);

while (m.find()) {
    m.appendReplacement(sb, replacements.get(m.group()));
}

m.appendTail(sb);

System.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)

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/02/20 17:10

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

    キャンセル

  • 2017/02/20 20:44

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

    キャンセル

  • 2017/02/21 11:14

    ベンチマークとってみたら、以下のようになりました。

    変換元文字列をランダムに生成したテキスト文字列220KBを処理

    元のコード:47ms
    上記コード:116ms

    キャンセル

  • 2017/02/21 11:19

    ご報告ありがとうございます。

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

    キャンセル

  • 2017/02/21 11:49 編集

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

    キャンセル

  • 2017/02/21 12:06 編集

    自分もやってみましたがそこまでの差はありませんでした。220Kのランダムテキストを用いて、2つをそれぞれメソッドとして定義、あらかじめ数回ループさせてキャッシュヒット状態やJIT効果がそれなりに効くように配慮した後で10回測定し平均と標準偏差を出すと以下のようになりました。(単位ms)

          平均 標準偏差
    -------------------------------------
    元のコード:25.2 4.6
    上記コード:25.9 5.7

    なお、Patternはあらかじめコンパイルしておける利点がありますが、自分はそれをあえてせずに毎回Map生成/Pattern.compileをさせてます。

    キャンセル

  • 2017/02/21 12:45

    KSwordOfHaste様

    検証ありがとうございます。

    私の手元の環境では常に、速い順に
     replace
     Pattern による1回のマッチング
     replaceAll

    となりましたが、Paiza.io で同じコードを繰り返し実行すると、
    ときどき上の順番が入れ替わることがありました。
    https://paiza.io/projects/AqtCaWjWlK5Dkx4ja_9tkg

    マシン性能や解析対象の文字列のサイズなどによっても、違いがあるのかも知れませんね。

    キャンセル

  • 2017/02/21 13:14

    > マシン性能や解析対象の文字列のサイズなどによっても、違いがあるのかも

    そうかも知れませんね。ところでreplaceもやってみましたが・・・

    replace  :33.4 9.2
    上記コード:30.6 5.7

    そもそも「上記コード」の平均値そのものが違ってます(汗)。同じ測定を複数回やるとreplace/上記コードが逆転する場合もありました。自分の測定も非常におおざっぱであったということでしょうねw;

    キャンセル

  • 2017/02/23 20: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;
    }

    }
    }

    }
    ```

    キャンセル

  • 2017/02/23 22:52

    ご報告ありがとうございます。

    このコードは、さすがに遅いはずです(笑)

    > 450KBのランダムなテキスト
    から str.charAt(s) で1文字ずつ取得しているため、
     1 + 2 + ... + n (n はテキストの文字数)
    という回数だけ、テキストのシークが行なわれるからです。

    キャンセル

  • 2017/02/23 23:26

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

    キャンセル

  • 2017/02/23 23:59 編集

    KSwordOfHaste様

    コメントありがとうございます。

    確かに、
    > String同士の連結
    がパフォーマンスに与える影響の方が大きそうですね。

    Java の場合、String型の連結は新たな Stringインスタンスを生成しますので。

    ただし、
    > 先頭から1文字ずつ取り出すコード自体は悪くない気がします。
    につきましては、判断を保留させていただきますw

    キャンセル

  • 2017/02/24 13:07 編集

    自分の意見を捕捉しておきますね~

    > 先頭から1文字ずつ取り出すコード自体は悪くない気がします。

    charAt(i)の処理時間はiの位置には依存せず常に固定です。またループは文字列の各要素アクセス回数は1回/要素なのでループ回数も最小限です。そのため「性能的には」悪くはないと思ったのでした。もちろん他の手段に比べて低水準な実装なので総合的にベストかと言われると?ではありますw

    キャンセル

  • 2017/02/26 14:15

    ご指摘どおりStringが遅かったです。
    ベタな書き方が一番はやい結果になりました。
    https://paiza.io/projects/-G3yz0B02ZWjbVlVFxY-nA

    replaceOneTime=378ms
    replaceManyTime=243ms
    replaceNoRegex=42ms

    あと、replaceAllやreplaceだと、\\\\rを\\rに変換後、\rにまで変換してしまうバグがありました。replaceでやるなら、\\\\を一旦、別の値に置き換えて(私は\1\1に置換した)、最後に置き換えた値を\\に変換するということをやる必要がありました。(もっとよい回避策があるかもしれませんが)

    いずれにしても、ベタな書き方が一番はやかったので、こちらを採用しようと思っています。いろいろご意見くださり、ありがとうございました。

    キャンセル

+1

訂正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 22: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
    ```

    キャンセル

  • 2017/02/19 23:21

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

    キャンセル

  • 2017/02/20 17:11

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

    キャンセル

  • 2017/02/20 20:43

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

    キャンセル

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

  • ただいまの回答率 90.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

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

  • Java

    14412questions

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