プロが教える店舗&オフィスのセキュリティ対策術

Perlで、UTF-8のテキストファイル内の文字列置換をワンライナーで行いたいのですが、可能でしょうか。

現状は、以下のような書式を実行しています。

perl -pi.bak -e "s/置換前/置換後/gi;" "入力.txt"

しかし、上記ですと、入力ファイルの文字コードがUTF-8では処理されません。

UTF-8のファイルを処理するにはどのようにしたら良いでしょうか。ワンライナーでは無理でしょうか。

環境は、Windows10、ActivePerl(perl 5, version 28, subversion 1 v5.28.1)です。

ご存じの方がおられましたら、お教え頂けると有難いです。

質問者からの補足コメント

  • 今回の質問の目的は、大量のUTF-8のHTMLファイルの文字列置換でして、世の中に複数ファイル対応の文字列置換ソフトはあるのですが、少し自分が欲しいものと細かい挙動等が違っていまして、出来ればスクリプトやバッチファイルで実現したいと思っています。

    次の補足に、作成中のバッチファイルのおおまかな例を書きます。

      補足日時:2019/10/17 19:16
  • ――――――――――――――

    @echo off

    rem ■ 設定
    set "対象フォルダ=C:\xxx"
    set 対象ファイル=*.txt
    set 置換処理=s/aaa/bbb/gi; s/ccc/ddd/gi;
    set 出力文字コード=w

    rem ■ バックアップ
    pushd "%対象フォルダ%"
    pushd ..\
    md "バックアップ"
    7za a "バックアップ\%date:/=%%time::=%.7z" "%対象フォルダ%"

    rem ■ 置換ループ
    for /r "%対象フォルダ%" %%a in (%対象ファイル%) do (
    nkf -w --overwrite "%%~a"
    onigsed -i.bak -R -e "%置換処理%" "%%~a"
    nkf -%出力文字コード% --overwrite "%%~a"
    )

    ――――――――――――――

      補足日時:2019/10/17 19:20
  • 補足したバッチファイルのように、スクリプトの上部に設定箇所があり、そこだけ書き換えれば動作するようにしたいです。

    間違えてShift_JISで保存しているHTMLファイルもあるかも知れないので、一応、置換前に「nkf」にて「UTF-8(BOM無し)」に変換し、置換後に、指定された出力文字コードに変換します。

    補足したバッチファイルを実行すると、「onigsed(sed)」の部分で、入力ファイルがUTF-8の為、うまく処理出来ません。ここにPerlのワンライナーを入れたいのです。

    現状、「mfind」というプログラムを使うと、UTF-8のファイルでも正常に置換出来ます。ただ、少し動作が重いです。

    ですので、Perlの方が高速かと思い、今回の質問をさせて頂いた次第です。

    これと同様の事が出来るのであれば、Perlやその他のスクリプト言語でも構いませんので、お教え頂けると有難いです。

      補足日時:2019/10/17 19:28

A 回答 (6件)

>現状、「mfind」というプログラムを使うと、UTF-8のファイルでも正常に置換出来ます。

ただ、少し動作が重いです。
No4です。
こちらで、perlの置換スクリプトを作成して、mfindとで実行時間を比較してみました。(時間計測はしていません)
mfindの場合、検索結果を画面に表示しているので、その為に遅くなっているように見えます。
画面出力をnullにリダイレクトすると圧倒的に早くなります。
perlの置換スクリプトよりはmfindのほうがかなり早いです。
perlのワンライナーとmfindが同等の感じでした。
その為、置換スクリプトが正規表現を使えたとしても、実行速度が遅いので今回の用途には使えないと考えます。
mfindにリダイレクト処理をいれて試されてはいかがでしょうか。
mfind /W /Q /E8N "/aaa/bbb/g" 入力.txt > null
のようにします。
mfindで十分対応可能かと考えます。
    • good
    • 0
この回答へのお礼

再度、ご回答ありがとうございます。

> aaaとかbbb個所に日本語が入ることはありますか?

「s/aaa/bbb/gi;」に日本語が入ることはあります。UTF-8の文字(「♡」等)が入る事はほぼ無いかと思います。

> 画面出力をnullにリダイレクトすると圧倒的に早くなります。

試してみましたが、おっしゃる通り、リダイレクトと「/Q」オプションを付けると、かなり高速化出来ました。ありがとうございます。

あと、for文によるループ処理ではなく、mfindの「/S」オプションでサブディレクトリを処理するようにしたら、更に高速化出来ました。

以前の私のmfindの記述ですと、処理に10倍以上の時間が掛かっていましたが、お教え頂いた改善で十分実用出来る速度になりました。この書式を使っていこうと思います。

おかげさまで解決しました。大変助かりました。本当にありがとうございました。他の方々も本当にありがとうございました。

お礼日時:2019/10/18 09:59

念のため確認ですが


set 置換処理=s/aaa/bbb/gi; s/ccc/ddd/gi;
のところですが、
aaaとかbbb個所に日本語が入ることはありますか?
例 s/東京/大阪/gi;
もし、に本語が入らないなら、
perl -CS -Mutf8 -pi.bak -e "s/aaa/bbb/gi;"
でOKかと。(aaa,bbbは正規表現可)

Malformed UTF-8 character: のエラーが出ているということは、日本語も入るということでしょうか。
    • good
    • 0

windows環境でワンライナーでのutf-8のファイルの文字置換は、無理があるようです。


スクリプトで良ければ、以下のようなスクリプトは提供可能です。
スクリプト名を仮にsed.plとすると
perl sed.pl 置換前 置換後 入力.txt 出力.txt
とすると、置換後の結果を 出力.txtへ出力します。(入力.txtは変更されません)
置換の仕方は s/置換前/置換後/gi と同じ結果になります。(但し正規表現は使用不可)
入力.txtは必ずutf-8で記述されていることが前提です(BOM無)
そのような仕様で良ければ提供可能です。
    • good
    • 0
この回答へのお礼

ご回答、ありがとうございます。

ご提案頂いたスクリプトの件ですが、大変有難いのですが、どうしても正規表現は使いたいので、今回は遠慮させて頂こうと思います。お気持ちに感謝致します。

詳しい経緯を書きたいのですが、お礼には文字数制限がありますので、補足にて書かせて頂こうと思います。ありがとうございました。

お礼日時:2019/10/17 18:59

まずは、エラーメッセージを理解しましょう。



Malformed UTF-8 character: \x91 (unexpected continuation byte 0x91, with no preceding start byte) at -e line 1.
というのは

\x91 というバイトコードがUTF-8として解釈しようとしたら不正だよ(Malformed UTF-8 character: \x91)
エラーが発生したプログラムは、 -e で指定された1行目だよ(at -e line 1.)

と言っています。
ということは
s/前/後/g
というスクリプトがUTF-8で書いてないから、理解できない、と言っているのです。

調べると、 前 のSHift_JIS は \x91\x4f です。
1バイト目がエラーメッセージの \x91 と一致します。

つまり、 WindowsのコマンドプロンプトからActive Perlのコマンドラインに -e でスクリプトを指定すると、Shift_JISで渡されるのでははないだろうか、という予想ができます。

> 2はもしかしたら大丈夫かもしれませんが、 \x{????} とコードポイントを使う必要があるかもしれません。

と書いたのは、この部分です。
s/\x{524d}/\x{5f8c}/
と、Unicodeのコードポイントで指定すれば動作するのではないでしょうか?


ワンライナーは諦める(日本語を使わないものに限定する)のが楽かと思います。
    • good
    • 0
この回答へのお礼

再度、ご回答ありがとうございます。

解説、非常に分かりやすかったです。本当にありがとうございます。

入力ファイルの文字コードや、ファイル名等が原因ではなかったのですね。勘違いしていました。

「s/\x{524d}/\x{5f8c}/」の件、試してみたのですが、エラーは出なくなったのですが、残念ながら置換は出来ませんでした。実際の記述は以下の通りです。

perl -pi.bak -CS -Mutf8 -pe "s/\x{524d}/\x{5f8c}/" "in.txt"

ちなみに、上記書式でも、「s/aaa/bbb/」という感じですと、対象がUTF-8のファイルでも置換出来ます。

もう少し色々と試してみます。ありがとうございました。

お礼日時:2019/10/17 07:27

今確認できる環境ではないのですが


考えられるものとして、次のことがあります。
1.入力ファイルがUTF-8だと解釈されていない
2.コマンドラインで書いた"s/置換前/置換後/gi;" が「期待通りの日本語」と解釈されていない。

1については、こちらで変更できそうです
https://perldoc.jp/docs/perl/5.26.1/perlrun.pod
> -C [number/list]
> -Cフラグは Perl Unicode 機能のいくつかを制御します。

2はもしかしたら大丈夫かもしれませんが、 \x{????} とコードポイントを使う必要があるかもしれません。
    • good
    • 0
この回答へのお礼

ご回答、ありがとうございます。

こちらで色々と試してみたのですが、私はPerlの知識がほとんど無いので、残念ながら1、2を解決するにはかなり時間が掛かりそうです。

ですので、実現が難しいようであれば今回は諦めて別のプログラムを利用する方法を考えようかと思います。ありがとうございました。

お礼日時:2019/10/16 16:03

この辺りを参考に


https://perldoc.jp/docs/perl/5.8.1/utf8.pod
https://perldoc.jp/docs/perl/5.26.1/perlrun.pod

おそらくは、以下で解決できるかと思います
perl -CS -Mutf8 -pe "s/前/後/g"
    • good
    • 0
この回答へのお礼

ご回答、ありがとうございます。

以下を実行しますと、

perl -pi.bak -CS -Mutf8 -pe "s/前/後/g" "in.txt"

以下のエラーが出ます。

Malformed UTF-8 character: \x91 (unexpected continuation byte 0x91, with no preceding start byte) at -e line 1.
Malformed UTF-8 character (fatal) at -e line 1.

入力ファイルの中身は、「前」1文字のみ記述されています。改行はありません。バイナリデータですと、「E5 89 8D」です。UTF-8(BOM無し)です。

お教え頂いたURLを拝見しましたが、Perlの知識がほとんど無い私には難し過ぎて理解するのに時間が掛かりそうです。

こちらでも色んなオプションや書式を試してはいるのですが、なかなかWEBにある書式のコピー&ペーストではうまくいかず、Perlを基礎から学んでいないと難しいようです。

ですので、実現が難しそうであれば別の方法を考えようと思います。ありがとうございました。

お礼日時:2019/10/16 15:50

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


このQ&Aを見た人がよく見るQ&A