🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

2回答

5649閲覧

数字文字列を3桁ずつカンマで区切る方法

退会済みユーザー

退会済みユーザー

総合スコア0

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

0グッド

0クリップ

投稿2019/10/09 08:51

初歩的な質問で申し訳ありませんが、ご教授ください。

金額の文字列でよくありますが、3桁ずつカンマで区切ります。
その際の処理は、正規表現による置換が一般的だと思います。
3つの言語で例を挙げます。

■PHP
$string = '1234567890';
$string = preg_replace('/(\d)(?=(\d{3})+(?!\d))/', '$1,', $string);

■JavaScript
var string = '1234567890';
string = string.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');

この2つについて、正規表現の「/(\d)(?=(\d{3})+(?!\d))/」の部分は、
数字1文字で始まり、その後に数字3文字が1回以上続き、その後に数字ではない文字がある、という
意味であることはわかるのですが、「$1,」がどうしてこれで文字列全体が3桁のカンマ区切りになるのか、
わかりません。
「$1」は最初にマッチした数字、例では「1」を表すと思うので、結果は「1,234567890」になるのかな、と
誤解しています。

■Ruby
string = '1234567890'
string = string.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse

こちらは文字列を反転させて後ろから置換するしかないそうですが、同様に「\1,」で置換しています。

正規表現についてきちんと勉強すべきことは重々承知した上で、ご教授いただけると助かります。
よろしくお願いいたします。

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

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

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

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

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

guest

回答2

0

後方参照という意味のようですね。
リンク内容

投稿2019/10/09 09:03

Naoko_Coco

総合スコア54

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

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

退会済みユーザー

退会済みユーザー

2019/10/10 06:42

さっそくコメントをありがとうございます!リンク先の内容も参考にして正規表現の理解を深めたいと思います。
Zuishin

2019/10/10 06:49

リンク先が間違っていると思います。後方参照は正規表現の中で使うものですが、これは置換で使うもので正規表現の一部ではありません。
退会済みユーザー

退会済みユーザー

2019/10/10 08:17

わざわざ教えていただき、ありがとうございます!
guest

0

ベストアンサー

PHPのpreg_replaceはオプションの指定が無い限りすべての合致する文字列を置換します
また、javascriptはg修飾子を利用することで同様にすべての合致する文字列を置換します
つまりヒットしたすべてを繰り返し置換するので結果としてカンマ区切りとなります

PHPの場合こうですね

PHP

1$string = '1234567890'; 2$string = preg_replace('/(?<=\d)(?=(\d{3})+$)/', ',', $string); 3print $string."<br>"; 4

肯定先読み・後読み
数字が前にあって、後ろに3桁の数字がある繰り返しで文字列の終わりまで

補足

こうするとわかりますか?

PHP

1$string = '1234567890'; 2$pattern='/(\d)(?=(?:\d{3})+(?!\d))/'; 3if(preg_match_all($pattern,$string,$matches)){ 4 print_r($matches); //1、4、7の箇所がヒットするこれを1,、4,、7,に置き換える 5 print preg_replace($pattern,'$1,',$string); 6}

ヒットした箇所が0のところで、ヒットする文字が1のところです
0を置換するとき1を利用しているに過ぎません
もっと簡易的にみれば$1をつかう必要もありません

PHP

1$string = '1234567890'; 2$pattern='/\d(?=(?:\d{3})+(?!\d))/'; 3if(preg_match_all($pattern,$string,$matches)){ 4 print_r($matches); 5 print preg_replace($pattern,'$0,',$string); 6}

投稿2019/10/09 09:01

編集2019/10/10 06:53
yambejp

総合スコア116690

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

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

退会済みユーザー

退会済みユーザー

2019/10/10 06:39

さっそくコメントをありがとうございます!おおむね理解できたのですが、まだ少し腑に落ちない面がありまして。。 PHPの「preg_replace」、Javascriptの「g修飾子」、Rubyの「gsub」はいずれも文字列全体をスキャンしてマッチする部分を置換するというのはわかるのですが、「$1,」や「\1,」がどうしても理解できません。 「/(\d)(?=(\d{3})+(?!\d))/」は肯定的先読みと否定的後読みで文字列全体の該当する部分を指しますが、「$1,」の「$1」とするには 「/((\d)(?=(\d{3})+(?!\d)))/」と正規表現全体を丸括弧にしないと置換することにはならないのでしょうか? 初歩的な例でいうと $string = '1234567890'; $string = preg_replace(/123(\d{3})7(\d{3})/, 'hoge$1foo$2', $string); print $string => 'hoge456foo890' のように$1は最初に、$2は次にマッチした部分になります。 なので、文字列全体で該当文字で「$1」で置換するには、正規表現全体を丸括弧で囲む必要はないのでしょうか? もちろん、そうではないのですから、私の理解不足に過ぎないのですが、教えていただけれると幸いです。
yambejp

2019/10/10 06:54

追記しておきました
退会済みユーザー

退会済みユーザー

2019/10/10 08:01

ご教授ありがとうございます! 例に使われている「?:」は知らなくて、後方参照不要の意味らしいですが、 これを「\d{3}」に適用している意味はなんでしょうか? というと別の内容の質問になってしまうので、それはそれとして。。 ヒットする箇所は文字列内の「1」「5」「8」ではないでしょうか?結果はこうなります。 $string = '1234567890'; preg_replace('/(\d)(?=\d{3})+(?!\d)/'. '$1,', $string); => '1,234,567,890' で、ヒットした箇所をすなわち「$1」で全置換している、というわけですね。少し理解が進みました。
yambejp

2019/10/10 08:25

> ?: (?:・・・)にしておくと$1とか$2のような余計なパターンが発生しなくなります > 「1」「5」「8」 いや1、5、8はありえないでしょう。3つおきなので、すくなくとも 2、5、8ですよね? とはいえ3文字の数字の前の数字だから 890の前の7 567の前の4 234の前の1 がヒットします
退会済みユーザー

退会済みユーザー

2019/10/10 10:57

なるほど!ようやくわかりました。肯定的先読み「ルックアヘッド.look a head=前を見ろ」の意味ですね。この辺りの理解をもう少し深めてみます。ご丁寧に教えていただき、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問