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

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

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

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

正規表現

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

Q&A

解決済

4回答

1116閲覧

エクセルのような正規表現(正負、小数点、ゼロ埋め、半角数値、カンマ位置)

pegy

総合スコア243

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

正規表現

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

0グッド

2クリップ

投稿2018/09/10 08:26

エクセルのセルを直接入力する近い感覚でinput type="text"を正規表現によりコントロールしようとしております。
条件としては

  1. 正負について、文頭の「-」は許可、「+」は許可しない
  2. ゼロ埋めは許可しない
  3. 文頭の小数点は許可しない
  4. 4桁以上の数字を入力する場合、3桁毎のカンマは許可する
  5. 上記の許可するものと半角数値を除き、許可しない

extra ) フォーカス中はカンマを表示しない

Javascript

1$(function(){ 2 $(document).on('focus','.text_input',function(){ 3 let num=$(this).val(); 4 num = num.replace(/,/g, ''); 5 $(this).val(num);//フォーカス中はカンマを表示しない 6 }) 7 8 $(document).on('blur','.text_input',function(){ 9 let num=$(this).val(); 10 if (num.match(/^[-]?([1-9]\d*|0)(.\d+)?$/)) { 11 num = num.replace(/(\d)(?=(\d\d\d)+$)/g, '$1,');//文頭マイナス許可、小数点文頭以外許可、ゼロ埋め不許可、それ以外の半角数値許可 12 $(this).val(num); 13 }else{ 14 $(this).val(''); 15 } 16 }) 17})

4)以外は上記の正規表現で満たすことに成功したのですが、4)がとても難しいです。
例えば、3桁以上の数値が入力されている状況で考えているのですが、0埋めや小数点の位置を加味すると
急に複雑化するため、頭を悩ませております。むしろこれに対応しているエクセルはすごいなとさえ思っているところです。。

2,33は許可しない
2,233は許可する
0,033等は許可しないの(ゼロ埋めを含む4桁は許可しない)
0.223,344は許可せず(小数点以下はカンマは付さない)
2,355.33は許可する(整数部分が4桁以上のため許可)

blurされたらはじくようにしたいのですが、良いアイデア等があればご助言を賜りたく存じます。
宜しくお願い申し上げます。

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

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

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

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

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

guest

回答4

0

ベストアンサー

こんにちは。

一度に複雑な判定をやり切る正規表現を書こうとすると、後からデバッグしたり、機能を追加、修正するときに大変そうなので、入力値のバリデーション全体をいくつかのステップに分け、各ステップでは、目的が明確なチェック、または置換を行うという方向で以下を作成しました。(動作を明確にするため、エラーメッセージを表示する <span> とCSSを追加しています。)

html

1<input class="text_input" /> 2<br /> 3<span id="error_message" />

css

1input { 2 border: 1px solid #444; 3 font-size: 32px; 4 width: 240px; 5} 6.valid { text-align: right; } 7.error { border: medium solid red; }

javascript

1 2$(document).on('focus', '.text_input', function() { 3 4 if (!$(this).hasClass('error')) { 5 let num = $(this).val(); 6 num = num.replace(/,/g, ''); 7 $(this).val(num); // フォーカス中はカンマを消去 8 } 9 10 // バリデーション結果を表すクラスを削除 11 $(this) 12 .removeClass('valid') 13 .removeClass('error'); 14 15 // エラーメッセージを消去 16 $('#error_message').text(''); 17}); 18 19$(document).on('input', '.text_input', function() { 20 if ($(this).val() === '') 21 $(this).data('comma-separated', false); 22}); 23 24const validate = (str, option) => { 25 26 // 最初にトリミングしておく 27 str = str.trim(); 28 29 if (str.length === 0) 30 return str; 31 32 // チェック1: 先頭にのみ-を許容し、半角数字と何個かのカンマと、0個または1個のピリオドで構成されること 33 let groups = /^([-]?)([0-9,]+)(.[0-9]*)?$/.exec(str); 34 35 if (!groups) 36 throw new Error('許容されない文字を含む。あるいは許容されない位置に数字以外の文字がある。'); 37 38 // チェック2: 整数部分が2文字以上の場合、0から始まっていないこと 39 if (/^0./.test(groups[2])) 40 throw new Error('整数部分が2文字以上であり、かつ0で始まっている。'); 41 42 // チェック3: 整数部分にカンマが含まれる場合、正しく下の桁から3桁ごとに区切られている。 43 if (groups[2].includes(',') && !/^[^,]{1,3}(,[^,]{3})+$/.test(groups[2])) { 44 throw new Error('整数部分のカンマ区切りが正しく3桁ごとになっていない。'); 45 } 46 47 // 置換1: 整数部分にカンマが含まれず、かつオプションでカンマ区切りが指定されていれば、カンマ区切りに変換 48 if (!groups[2].includes(',') && option.commaSeparated) 49 groups[2] = Number(groups[2]).toLocaleString('ja-JP'); 50 51 // 置換2: 小数部分が存在し、かつ、小数部分の末尾が1個以上の0の場合、この末尾の0を削除する。 52 if (groups[3] && groups[3].length > 1) { 53 const m = /^.([0-9]*[1-9])?(0+)$/.exec(groups[3]); 54 if (m) 55 groups[3] = (m[1] ? '.' + m[1] : ''); 56 } 57 58 // 置換3: 小数点はあるが、少数点の後ろに数字がない場合、小数部分を空文字列にする。 59 if (groups[3] === '.') 60 groups[3] = ''; 61 62 return groups[1] + groups[2] + (groups[3] || ''); 63}; 64 65 66$(document).on('blur', '.text_input', function() { 67 68 try { 69 // 入力値をチェックし、かつ不要な文字を除去した文字列を取得 70 const str = validate( 71 $(this).val(), 72 { commaSeparated: $(this).data('comma-separated') } 73 ); 74 75 // チェックが通った場合、置換後の文字列をinputに設定 76 $(this).val(str) 77 78 // 入力文字列が空ではない場合、エラーの通ったことを示すクラスを追加 79 if (str.length > 0) 80 $(this).addClass('valid'); 81 82 // セルの表示状態をカンマありにするかどうかのフラグ設定 83 $(this).data('comma-separated', str.includes(',')); 84 85 } catch (e) { 86 // エラー状態を示すクラスを追加 87 $(this).addClass('error'); 88 89 // エラーメッセージを表示 90 $('#error_message').text(e.message); 91 } 92});

上記のコードを以下に上げていますので、実際に入力してお試し頂ければと思います。

https://jsfiddle.net/jun68ykt/nym6bxLh/3/

動作について、ご質問にある

  1. 正負について、文頭の「-」は許可、「+」は許可しない
  1. ゼロ埋めは許可しない
  2. 文頭の小数点は許可しない
  3. 4桁以上の数字を入力する場合、3桁毎のカンマは許可する
  4. 上記の許可するものと半角数値を除き、許可しない

extra ) フォーカス中はカンマを表示しない

は満たせているのではないかと思います。さらに上記に加えて、以下のように動作します

  1. 数値を入力してからフォーカスを外し、入力値の検査後、入力値に
  • 問題がなければ右寄せで表示される。(Excelと同様)
  • 問題があると、<input>が赤い線で囲まれ、かつエラーメッセージが表示される。
  1. 少数点はあっても小数点以下の数字がない場合、たとえば 55. と入力してフォーカスを外すと、少数点は除去されて、55と表示される。(Excelと同様)

  
3) 初めにカンマ区切りの数字、たとえば 123,555 を入力して一度フォーカスを外してから、再度、フォーカスすると、カンマのない文字列になるが、何らかの修正後に再度フォーカスを外し、値に問題がない場合、カンマ区切り表示に戻る。その後、いったん空欄にすると、カンマ区切り表示モードは解除される。

  1. 小数点以下の数字の末尾にいくつかの 0 がある場合、たとえば 12.340500と入力してフォーカスを外すと、末尾の余分な 0 は除去されて,12.3405と表示される。(Excelと同様)

以上です。

pegyさんの要件、あるいはご要望どおりではないところが何かあれば、コメントからお知らせください。

投稿2018/09/10 16:00

jun68ykt

総合スコア9058

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

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

pegy

2018/09/11 06:10

jun68ykt様 詳細なコードやご解説までのことして頂き、本当に有難うございます。 そしてこの質問のために、多くの時間を割いて頂きましたことにお詫びをさせてください。 また、ご考慮頂いた中でも2)の小数点を打った数字以下の小数が存在しない場合や4)で小数点の最下位の"0"等そのようなケースにまで全く考えが及んでおりませんでした。 ユーザー入力について、想定すべき可能性やバリデーションについて、もっともっと検討が必要で勉強と経験をしなければならないと痛感させられました。 本当に勉強になりました。 改めまして、厚い御礼を申し上げます。
jun68ykt

2018/09/11 13:16

pegyさん どういたしまして。 このような入力値の形式にそったバリデーションを自作する場合、作って動かしてみることで、他にも対応しなければならない入力パターンがあることに気がつき、 > 考えが及んでおりませんでした。 というケースに遭遇することはよくあることです。なのでそのたびに少しずつ機能拡張したり、既存機能を修正することになりますから、1つずつ足していく機能単位(ここでは、3つの検証と3つの置換の6個)の個々を、取り外したり、別のものを足したりがしやすいように見通しよく作っておき、新しい1個を追加するコードを書いている途中で袋小路にハマってうまくいかなくなっても、それを追加する前の間違いなく動いている状態にすぐに戻せるような作り方をするとよいかと思います。
guest

0

セルの書式設定

extra ) フォーカス中はカンマを表示しない

ということは、blur イベント発火中はカンマがありませんので、「4)4桁以上の数字を入力する場合、3桁毎のカンマは許可する」の条件が不要ではありませんか。

JavaScript

1/^-?(?:[1-9]\d*|0)(?:.\d*[1-9])?$/

ゼロサプレス

  1. ゼロ埋めは許可しない

Excel ではゼロパディングはゼロサプレスに変換されますが、ゼロパディングの入力は許可されています。

JavaScript

1string.replace(/^(-?)0*((?:[1-9]\d*|0)(?:.\d*[1-9])?)0*$/, '$1$2');

Re: pegy さん

投稿2018/09/10 10:56

編集2018/09/10 10:57
think49

総合スコア18162

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

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

pegy

2018/09/11 06:01

think49様 コメント頂き有難うございます。 >セルの書式設定 こちらについては、実はカンマ付きの数字をコピペした時に123,456を正しくコピペしているにも関らず、許可されなくなってしまうという問題でございました。当初から全ての状況を想定しお尋ねすべきところであったのですが、やりながらこんなケースもあるというところに気づき混乱をさせてしまい申し訳ございません。ただし、こちらはblur以外のコピペに対応するイベントを設定することで対応できそうです。 >ゼロサプレス についてもありがとうございます。確かに0からはじまる数字を入力した場合、0以外の数値がその後入力次第で許可されますね。 改めて御礼申し上げます。
guest

0

正規表現では微妙なので
一度カンマを取ったものを数値化しカンマフォーマットをかけたものと
元の値を比べるとどうでしょうか?

javascript

1["12345678","12,345,678","12345,678","1,2345,678","2,33","2,233","0,033","0.223,344","2,355.3344"].forEach(function(x1){ 2 var x2=parseFloat(x1.replace(/,/g,"")).toString().replace(/^(-?[0-9]+)(?=.|$)/, function(s){ return s.replace(/([0-9]+?)(?=(?:[0-9]{3})+$)/g, '$1,')}); 3 console.log(x1+((x1===x2||x1===x2.replace(/,/g,""))?"ok":"ng")); 4});

投稿2018/09/10 09:39

yambejp

総合スコア114814

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

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

pegy

2018/09/11 06:03

yambejp様 なるほど、その様なアプローチは全く思いつきませんでした。 こちらの方法で実装してみようと思います。 大変勉強になりました。
guest

0

これでどうでしょうか?

JavaScript

1/^(?:0|-?[1-9]\d{0,2}(?:,?\d{3})*)(?:.\d*[1-9])?$/

投稿2018/09/10 08:54

x_x

総合スコア13749

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

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

yambejp

2018/09/10 09:14

難しいところですが、「1234,567」のような中途半端なカンマも 通っちゃいますね。
x_x

2018/09/10 09:21

「エクセルのような」ということなので、いいのではないでしょうか?
pegy

2018/09/10 09:30

コメント有難うございます。 ご指摘の盲点に気が付かず、申し訳ございません。結論としては、許可しないです。7桁以上であって、カンマが一つしかないも許可をせず、エクセルも強制的に文字列認識であったと思います。最後の点、認識違いがあれば申し訳ございません。
x_x

2018/09/10 09:31

一応Excelで確認したうえでの投稿ですが、許可しないなら該当箇所の?を外せばいいのでは?
yambejp

2018/09/10 09:41

Excelでは「1234,567」は「1,234,567」に変換されるから 「エクセルのような」という意味で言えばNGでしょう
x_x

2018/09/11 00:23

それはreplaceのほうの話では? この正規表現は「はじく」ほうの話のみです
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問