- ベストアンサー
3回以上連続する文字(どんな文字でも)を削除する方法
3回以上連続する文字(どんな文字でも)を削除する方法 お世話になります。 掲示板等の文字列で、、、 ■■■■■■■■ や ========= といった連続する文字列を削除したいと思っています。 その際「3回以上、同じ文字がつづいているものは、根こそぎ削除」という形にしたいと思い、 ~s///g; を用いて、あれこれやっているのですが (また、ぐぐって、あれこれ調べたのですが) 全く、うまくいきません。 どの様にすれば、うまくいくのか、アドバイスいただければ幸いです。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
No.2のTacosanがおっしゃるように、decode、encodeをするのが正攻法だと思います。 use utf8 の代わりに use encoding 'cp932' という方法もあるかもしれませんが、これは超非推奨なのでやめたほうがいいでしょう。 Shift-JIS限定なら以下の正規表現でいけそうな気もします(あまり自信なし)。 $moji =~ s/([\x00-\x7F\xA1-\xDF]|[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC])\1\1+//g;
その他の回答 (3)
- Tacosan
- ベストアンサー率23% (3656/15482)
えぇと.... まず正規表現で「.」は「任意の 1文字」にマッチします (改行にマッチするかどうかは状況による). で, 任意の正規表現のうしろに「*」を付けると「直前のものが 0個以上ある」という意味を持ちます. なので, 「.*」で「『任意の 1文字』が『0個以上ある』」, つまり「(0個以上の文字からなる) 任意の文字列」にマッチするわけです. でもって, 正規表現をかっこでくくると「後で使うかもしれないからその中のものを覚えておく」という働きもあります. 全体で「(.)」だと「任意の 1文字にマッチして, それを記憶する」ということになります. この「記憶しておいたもの」を*パターン中で*使うときに「\数字」という形を使います. ここの「数字」は「最初からかぞえた開きかっこの数」に対応します. 置換文字列 (s/// のうしろのところ) で使うときは, 「$数字」の形で参照します. と, 正規表現は「簡単なものを組合せて複雑なパターンを作る」ようになっています. なので, 「* がどういう意味を持つのか」「() はどういうときに使うのか」というように理解すべきです. 備忘録のように 「.* 」が、何かの文字列にマッチさせたい時に使う、 「([^<]*) 」が、どんな複数の文字列でもマッチさせる時に使う などとしてしまうと応用がききません.
お礼
詳しく教えていただき、ありがとうございます!!! >応用がききません. もう、おっしゃるとおりで。。。。(^_^;) 昔、少しだけかじった事のあるBasic言語の類推で、掲示板プログラムのちょっとした改造から始め、最近は、各種APIを活用したウェブサービスも、改造、改造で作れる様にはなったのですが、なにせ、手におえない状況になった時に、その都度、ぐぐって(それでも手に終えない時に、ここにお世話になっています)解決策を探る、という手法でやってきたので(Perlの本も3冊買って読んだのですが、具体的に取り組まないと頭に入ってこなくて(つまり、基礎の基礎があまり身についていない))、おっしゃるとおり、応用が効かず、その都度の対応策をPerl備忘録に書き込み、その場をしのいできました。 >正規表現は「簡単なものを組合せて複雑なパターンを作る」ようになっています なるほど。。。。。 今まで、例えば「([^<]*)」等という表現を見るにつけ、その見た目だけで、これはもう、自分の頭を超える手法だ!と直感的に思い込み、基礎を頭に叩き込もうとせず、その都度、その都度の具体的な処理例ばかりおいかけてきたのですが、今一度、「簡単なもの」「基礎の基礎」を頭に入れてみようと思いました。 ご指摘、本当にありがとうございます! とゆーことで、今日、教わった基礎的な事を、ここに記させていただきます。 ●「.」は「任意の 1文字」にマッチする。 ●正規表現をかっこでくくると「後で使うかもしれないからその中のものを覚えておく」という働きがある。 ●正規表現は簡単なものを組合せて複雑なパターンを作る様になっている。 (基礎的なもの、簡単なものは記憶すべし) ●入力時の文字コードに合わせた処理をしないと、動くものも動かない。 それにしても、いろいろとアドバイスをしていただいた、sara9647さん、Tacosanさん、ryu_chanさん、本当にありがとうございました! 心より感謝致します。
- Tacosan
- ベストアンサー率23% (3656/15482)
入力ファイルが shift_jis なら, Encode::decode を使って Unicode にすればいいんじゃないかな. 出力の際に Encode::encode する必要はあるかもしれん. マッチパターンの「\1」は「最初の開きかっこの部分にマッチした文字列」を意味します. これだけわかれば (.)\1\1 で「同じ文字が 3回続いている」ことがわかるかとおもいます... あれ? 3回「以上」なら (.)\1\1+ か? あと, 「~s///g;」じゃないからね. この「~」はその前にある演算子 (=~ や !~) の一部なので切り離しちゃダメ.
お礼
>マッチパターンの「\1」は「最初の開きかっこの部分にマッチした文字列」を意味します. なるほど。そーゆーことでしたか。 納得しました。 ご教授いただき、ありがとうございます。 (改めて、Perlの奥深さに、感嘆しています。 また、「同じ文字が 3回以上続いているものを削除」という、少しだけやっかいな処理をしなければならない様に見えるものを「s/(.)\1\1//g;」等というシンプルな表現で処理できてしまうPerlの懐の深さ?も、改めて感じました。Tacosanさん、面白さを教えていただき、ありがとうございます) >「~s///g;」じゃないからね. この「~」はその前にある演算子 (=~ や !~) の一部なので切り離しちゃダメ. $moji=~s/(.)\1\1//g; は、いいけれど、 $moji=~ s/(.)\1\1//g; はダメ、ということでしょうか。 今まで、ずっと「~s///g」を1つのセットだと思い込んでいました。。。。 Perlの基礎知識を補正していただき、ありがとうございます!
補足
※訂正 $moji=~s/(.)\1\1//g; は、いいけれど、 $moji=~ s/(.)\1\1//g; は駄目 ではなく、、、 $moji=~ s/(.)\1\1//g; は、いいけれど、 $moji= ~s/(.)\1\1//g; は駄目
s/(.)\1\1//g 但し文字コードには注意 use utf8なりuse encoding ""がないと所謂全角文字に関して処理が出来ない。
補足
アドバイス、ありがとうございます!! ●use utf8宣言し ●プログラム書類自体をutf8エンコーディングで保存した上で ●s/(.)\1\1//g を走らせたところ、見事に3文字以上のものが削除されました! ありがとうございます! その上でのことなのですが、、、 ●「shiftjis」エンコーディングで保存されているプログラムファイルに組み込む場合は、どうしたらよいのでしょうか?(組み込みたいプログラム書類自体が既に「shiftjis」エンコーディングで保存されている上に、この設定を変更できない(変更すると、うまく稼働しなくなります)、こんがらがった状況になっています) もしくは、「shiftjis」エンコーディングで保存されているプログラムファイルでは、原理的に実現不可能なことでしょうか?(尚、サーバーのperlのバージョンはver.5.8.* です) ●s/(.)\1\1//g のどこにも「3」という数字が入っていなのですが、どうして、これで「3回以上の文字のみ」マッチさせる事ができるのでしょうか?(実際に、おっしゃるとおりに稼働したので驚いています。理由を調べてみたのですが、分かりませんでした、、、) 尚、私がテスト的に走らせたプログラムは以下です。 (実際に、組み込みたい「shiftjis」エンコーディングで保存されているプログラム書類は、これとは別にあります(^_^;)) --------------------------------- #!/usr/local/bin/perl use utf8; print <<"HTML"; Content-type: text/html <html> HTML $moji="aiiuuueeeeoooooooooooo"; $moji=~s/(.)\1\1//g; print $moji."<br><br>"; $moji="あいいうううええええおおおおおかかかかかかかかかかかかか"; $moji=~s/(.)\1\1//g; print $moji."<br><br>"; $moji="★★■■■□□□□◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆"; $moji=~s/(.)\1\1//g; print $moji."<br><br>"; --------------------------------- 結果 --------------------------------- aiie あいいえおおか ★★□ ---------------------------------
補足
ryu_chanさん、アドバイスいただき、ありがとうございます!! >Shift-JIS限定なら以下の正規表現でいけそうな気もします(あまり自信なし)。 実は、今一度確認してみたところ、てっきり、Shift-JISエンコード保存だと思い込んでいた、融合させたい私の既存プログラム書類は、「日本語(MacOS)エンコード」という、独特な?(Shit-jisの亜種?)エンコードで保存されている事が分かりました。(^_^;) で、このエンコードの場合、「\」は使えず「¥」で表現する様になっており、上記書かれているものを、¥に変換した、、、 $moji =~s/([¥x00-¥x7F¥xA1-¥xDF]|[¥x81-¥x9F¥xE0-¥xFC][¥x40-¥x7E¥x80-¥xFC])¥1¥1+//g; で、うまくいきました!! ありがとうございます! (どこかのサイトで、「¥」は使うべきではない!という記述を見かけたことがあるのですが、、、、w) で、上記式は、sara9647さんが提示されている「s/(.)\1\1//g」の「(.)」の部分に、「([\x00-\x7F\xA1-\xDF]|[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]」を当てはめたものなのですね。 で、ぐぐったところ、「\x00-\x7F\xA1-\xDF」は1バイトSHIT-JIS文字、「[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC])」は2バイト SJIS文字ということが分かり、このどちらかの文字クラスの3回以上の連続にマッチした場合、削除する、というやり方の様ですね(解釈が間違っていたらご指摘下さい) とした場合、sara9647さんが使われている「(.)」の意味も知っておきたいのですが、いくら、ぐぐっても、これが分かりませんでした(グーグルは、こーいった記号のみの検索で威力が発揮できない様で、、、、「Perl (.) マッチ 」でも、何もひっかかってきません。。。 私のPerlメモ帳(備忘録)には、、、、 「.* 」が、何かの文字列にマッチさせたい時に使うものとあり、 「([^<]*) 」が、どんな複数の文字列でもマッチさせる時に使うもの、とあります。 ですので、てっきりsara9647さんの式は、、、 s/(.*)\1\1//g だと思っていたのですが、実際は、、、 s/(.)\1\1//g なのですね。 できましたら、その理由などもご教授いただければ幸いです。 ※尚、ryu_chanさんのプログラムと、その表示結果を以下記しておきます。 ----------------------------- #!/usr/local/bin/perl print <<"HTML"; Content-type: text/html <html> HTML $moji="aiiuuueeeeoooooooooooo"; $moji =~s/([¥x00-¥x7F¥xA1-¥xDF]|[¥x81-¥x9F¥xE0-¥xFC][¥x40-¥x7E¥x80-¥xFC])¥1¥1+//g; print $moji."<br><br>"; $moji="あいいうううええええおおおおおかかかかかかかかかかかかか"; $moji =~s/([¥x00-¥x7F¥xA1-¥xDF]|[¥x81-¥x9F¥xE0-¥xFC][¥x40-¥x7E¥x80-¥xFC])¥1¥1+//g; print $moji."<br><br>"; $moji="★★■■■□□□□◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆"; $moji =~s/([¥x00-¥x7F¥xA1-¥xDF]|[¥x81-¥x9F¥xE0-¥xFC][¥x40-¥x7E¥x80-¥xFC])¥1¥1+//g; print $moji."<br><br>"; ----------------------------- 表示結果 ----------------------------- aii あいい ★★ ----------------------------- ※3文字以上のものは「根こそぎ」削除される理想のプログラムでした。 (ryu_chanさん、重ね重ね、教えていただき、ありがとうございます!)