前提・実現したいこと
Railsで、json
のファイルをアップロードし、その値をPostgresql
のテーブルに更新するような処理を考えています。
この際に、json
の値が適当であるかをjsonschema
を利用してチェックしています。
発生している問題・エラーメッセージ
例えばcharacter varying(40)
の型のカラムに更新しようとした場合、
全角20、半角40文字を超えるようなものはエラーにしたいと思います。(混合の場合も含む)
これを、jsonschema
で弾こうと考え、maxlength
オプションに指定してみましたが、単純な文字列での
バリデーションになってしまい、半角、全角両者とも40文字まで入力可能になってしまいます。
{ "type": "object", "properties": { "column1": { "type": "string", "minLength": 0, "maxLength": 40 } } }
試したこと
jsonschema
について調べて見た所、pattern
というオプションが書けるようで、ここに書いた正規表現でバリデーションを
かけれるようでした。参考
なので、全角2文字、半角を1文字として、合計最大40文字以内か?
という正規表現がかければ問題が解決すると考えています。
こういった複雑なものは正規表現でかけるでしょうか?
補足情報(FW/ツールのバージョンなど)
RubyOnRails 5
Postgresql 10
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答7件
0
rubyで、になりますが
正規表現じゃ無くちゃ駄目ですか(れんぽう)
"1svあ1#".codepoints.group_by{|c| c< 200}.map{|c,a| [c,a.size]}
=> [[true, 4], [false, 2]]
判断基準の200はいい加減に入れたので、ここは調べて下さい。
128なのか256なのか、、、、
投稿2019/07/03 21:14
編集2019/07/03 21:20総合スコア23645
0
正規表現
「全角文字 = 2文字固定」のコードは下記になりますが、良い実装とは思いません。
Ruby
1str = 'abcあいうえおdef'; 2halfSize = str.scan(/[\x00-\x7F]/).size; 3size = halfSize + (str.size - halfSize) * 2 4print(size)
bytesize
バイト数を測るなら、bytesize
を使うのが適切と思います。
Ruby
1require 'kconv' 2print(Kconv.tosjis('abcあいうえおdef').bytesize)
要件の「全角 = 2文字」を設定する為に、Shift_JIS に変換していますが、本来の文字コードでバイト数を測るのが正道と思います。
もし、本来の文字コードが UTF-8 であったのなら、上記コードは 21byte になる為、「本来のバイト数」と「計測したバイト数」に6byte分のズレが生じます。
Re: hiepita1 さん
投稿2019/07/06 23:33
編集2019/07/06 23:40総合スコア18194
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
たぶん可能ですが、恐ろしく長くなります。問題点は二つです。
全角のクラスと半角のクラス
文字が全角か半角はUnicodeで資料があります。
https://www.unicode.org/Public/12.1.0/ucd/EastAsianWidth.txt
これを元に全角文字のみにマッチする正規表現クラスを作ってみましょう。
Ruby
1#!/usr/bin/env ruby 2# frozen_string_literal: true 3 4# EastAsianWidth.txt をunicode.orgから取得しておくこと。 5# 現在の最新は下記URLから取得できる。(13.0.0にはない?) 6# https://www.unicode.org/Public/12.1.0/ucd/EastAsianWidth.txt 7 8def full_half_code(file, ambiguaus: :full) 9 10 full_width = [] 11 half_width = [] 12 ambiguaus_width = {full: full_width, half: half_width}[ambiguaus] 13 raise ArgumentError if ambiguaus_width.nil? 14 15 props = { 16 'A' => ambiguaus_width, 17 'F' => full_width, 18 'H' => half_width, 19 'N' => half_width, 20 'Na' => half_width, 21 'W' => full_width, 22 } 23 24 IO.foreach('EastAsianWidth.txt') do |line| 25 line.sub!(/\#.*$/, '') 26 line.strip! 27 next if line.empty? 28 29 unless line =~ /^\h{4,6}(?:..\h{4,6})?;(?:A|F|H|N|Na|W)$/ 30 raise "invalid line #{line}" 31 end 32 33 chars, prop = line.split(';') 34 if chars =~ /^(\h{4,6})..(\h{4,6})$/ 35 char_range = $1.hex..$2.hex 36 else 37 char_range = chars.hex..chars.hex 38 end 39 40 list = props[prop] 41 raise "bug? no list for #{prop}" if list.nil? 42 43 if list.empty? 44 list << char_range 45 elsif list.last.end != char_range.begin 46 list << char_range 47 else 48 new_last_range = list.last.begin..char_range.end 49 list[-1] = new_last_range 50 end 51 end 52 53 { 54 full: full_width, 55 half: half_width, 56 } 57end 58 59def make_char_class_and_not(list) 60 str = +'' 61 list.each do |range| 62 if range.size == 1 63 str << "\u{#{range.begin.to_s(16)}}" 64 else 65 str << "\u{#{range.begin.to_s(16)}}-\u{#{range.end.to_s(16)}}" 66 end 67 end 68 [ 69 "[#{str}]", 70 "[^#{str}]", 71 ] 72end 73 74if $0 == __FILE__ 75 # 「α」等のギリシャ文字はフォントによって半角・全角が異なる。 76 # 仕様に合わせて変数を変更して調整すること。 77 # 全角として扱う: :full 78 # 半角として扱う: :half 79 ambiguaus = :half 80 81 codes = full_half_code('EastAsianWidth.txt', ambiguaus: ambiguaus) 82 83 # 全角の方が少ないのでそれを用いて文字コードの部品作り 84 full_class, half_class = make_char_class_and_not(codes[:full]) 85 puts full_class 86end
「α」等はフォントによって幅が異なりますが、今回は半角としました。得られた文字クラスは下記になります。
[\u{1100}-\u{115f}\u{231a}-\u{231b}\u{2329}\u{232a}\u{23e9}-\u{23ec}\u{23f0}\u{23f3}\u{25fd}-\u{25fe}\u{2614}-\u{2615}\u{2648}-\u{2653}\u{267f}\u{2693}\u{26a1}\u{26aa}-\u{26ab}\u{26bd}-\u{26be}\u{26c4}-\u{26c5}\u{26ce}\u{26d4}\u{26ea}\u{26f2}-\u{26f3}\u{26f5}\u{26fa}\u{26fd}\u{2705}\u{270a}-\u{270b}\u{2728}\u{274c}\u{274e}\u{2753}-\u{2755}\u{2757}\u{2795}-\u{2797}\u{27b0}\u{27bf}\u{2b1b}-\u{2b1c}\u{2b50}\u{2b55}\u{2e80}-\u{2e99}\u{2e9b}-\u{2ef3}\u{2f00}-\u{2fd5}\u{2ff0}-\u{2ffb}\u{3000}\u{3001}-\u{3003}\u{3004}\u{3005}\u{3006}\u{3007}\u{3008}\u{3009}\u{300a}\u{300b}\u{300c}\u{300d}\u{300e}\u{300f}\u{3010}\u{3011}\u{3012}-\u{3013}\u{3014}\u{3015}\u{3016}\u{3017}\u{3018}\u{3019}\u{301a}\u{301b}\u{301c}\u{301d}\u{301e}-\u{301f}\u{3020}\u{3021}-\u{3029}\u{302a}-\u{302d}\u{302e}-\u{302f}\u{3030}\u{3031}-\u{3035}\u{3036}-\u{3037}\u{3038}-\u{303a}\u{303b}\u{303c}\u{303d}\u{303e}\u{3041}-\u{3096}\u{3099}-\u{309a}\u{309b}-\u{309c}\u{309d}-\u{309e}\u{309f}\u{30a0}\u{30a1}-\u{30fa}\u{30fb}\u{30fc}-\u{30fe}\u{30ff}\u{3105}-\u{312f}\u{3131}-\u{318e}\u{3190}-\u{3191}\u{3192}-\u{3195}\u{3196}-\u{319f}\u{31a0}-\u{31ba}\u{31c0}-\u{31e3}\u{31f0}-\u{31ff}\u{3200}-\u{321e}\u{3220}-\u{3229}\u{322a}-\u{3247}\u{3250}\u{3251}-\u{325f}\u{3260}-\u{327f}\u{3280}-\u{3289}\u{328a}-\u{32b0}\u{32b1}-\u{32bf}\u{32c0}-\u{32ff}\u{3300}-\u{33ff}\u{3400}-\u{4db5}\u{4db6}-\u{4dbf}\u{4e00}-\u{9fef}\u{9ff0}-\u{9fff}\u{a000}-\u{a014}\u{a015}\u{a016}-\u{a48c}\u{a490}-\u{a4c6}\u{a960}-\u{a97c}\u{ac00}-\u{d7a3}\u{f900}-\u{fa6d}\u{fa6e}-\u{fa6f}\u{fa70}-\u{fad9}\u{fada}-\u{faff}\u{fe10}-\u{fe16}\u{fe17}\u{fe18}\u{fe19}\u{fe30}\u{fe31}-\u{fe32}\u{fe33}-\u{fe34}\u{fe35}\u{fe36}\u{fe37}\u{fe38}\u{fe39}\u{fe3a}\u{fe3b}\u{fe3c}\u{fe3d}\u{fe3e}\u{fe3f}\u{fe40}\u{fe41}\u{fe42}\u{fe43}\u{fe44}\u{fe45}-\u{fe46}\u{fe47}\u{fe48}\u{fe49}-\u{fe4c}\u{fe4d}-\u{fe4f}\u{fe50}-\u{fe52}\u{fe54}-\u{fe57}\u{fe58}\u{fe59}\u{fe5a}\u{fe5b}\u{fe5c}\u{fe5d}\u{fe5e}\u{fe5f}-\u{fe61}\u{fe62}\u{fe63}\u{fe64}-\u{fe66}\u{fe68}\u{fe69}\u{fe6a}-\u{fe6b}\u{ff01}-\u{ff03}\u{ff04}\u{ff05}-\u{ff07}\u{ff08}\u{ff09}\u{ff0a}\u{ff0b}\u{ff0c}\u{ff0d}\u{ff0e}-\u{ff0f}\u{ff10}-\u{ff19}\u{ff1a}-\u{ff1b}\u{ff1c}-\u{ff1e}\u{ff1f}-\u{ff20}\u{ff21}-\u{ff3a}\u{ff3b}\u{ff3c}\u{ff3d}\u{ff3e}\u{ff3f}\u{ff40}\u{ff41}-\u{ff5a}\u{ff5b}\u{ff5c}\u{ff5d}\u{ff5e}\u{ff5f}\u{ff60}\u{ffe0}-\u{ffe1}\u{ffe2}\u{ffe3}\u{ffe4}\u{ffe5}-\u{ffe6}\u{16fe0}-\u{16fe1}\u{16fe2}\u{16fe3}\u{17000}-\u{187f7}\u{18800}-\u{18af2}\u{1b000}-\u{1b0ff}\u{1b100}-\u{1b11e}\u{1b150}-\u{1b152}\u{1b164}-\u{1b167}\u{1b170}-\u{1b2fb}\u{1f004}\u{1f0cf}\u{1f18e}\u{1f191}-\u{1f19a}\u{1f200}-\u{1f202}\u{1f210}-\u{1f23b}\u{1f240}-\u{1f248}\u{1f250}-\u{1f251}\u{1f260}-\u{1f265}\u{1f300}-\u{1f320}\u{1f32d}-\u{1f335}\u{1f337}-\u{1f37c}\u{1f37e}-\u{1f393}\u{1f3a0}-\u{1f3ca}\u{1f3cf}-\u{1f3d3}\u{1f3e0}-\u{1f3f0}\u{1f3f4}\u{1f3f8}-\u{1f3fa}\u{1f3fb}-\u{1f3ff}\u{1f400}-\u{1f43e}\u{1f440}\u{1f442}-\u{1f4fc}\u{1f4ff}-\u{1f53d}\u{1f54b}-\u{1f54e}\u{1f550}-\u{1f567}\u{1f57a}\u{1f595}-\u{1f596}\u{1f5a4}\u{1f5fb}-\u{1f5ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6c5}\u{1f6cc}\u{1f6d0}-\u{1f6d2}\u{1f6d5}\u{1f6eb}-\u{1f6ec}\u{1f6f4}-\u{1f6fa}\u{1f7e0}-\u{1f7eb}\u{1f90d}-\u{1f971}\u{1f973}-\u{1f976}\u{1f97a}-\u{1f9a2}\u{1f9a5}-\u{1f9aa}\u{1f9ae}-\u{1f9ca}\u{1f9cd}-\u{1f9ff}\u{1fa70}-\u{1fa73}\u{1fa78}-\u{1fa7a}\u{1fa80}-\u{1fa82}\u{1fa90}-\u{1fa95}\u{20000}-\u{2a6d6}\u{2a6d7}-\u{2a6ff}\u{2a700}-\u{2b734}\u{2b735}-\u{2b73f}\u{2b740}-\u{2b81d}\u{2b81e}-\u{2b81f}\u{2b820}-\u{2cea1}\u{2cea2}-\u{2ceaf}\u{2ceb0}-\u{2ebe0}\u{2ebe1}-\u{2f7ff}\u{2f800}-\u{2fa1d}\u{2fa1e}-\u{2fa1f}\u{2fa20}-\u{2fffd}\u{30000}-\u{3fffd}]
半角は^
を付ければ良いでしょう。なお、これで一文字です。この時点でちょっと嫌になります。
単純に全角1個半角2個をセットで見ていけばいいわけでは無い。
まずは、簡単にするために、半角8文字分で考えてみます。したのh
は半角の文字クラスや文字、全
は全角の文字クラスや文字を表します。(重複しているパターンは省いています)
/^(hh|全){4}$/ hhhhhhhh hhhhhh全 hhhh全hh hhhh全全 hh全hhhh hh全hh全 hh全全hh hh全全全 全hhhhhh 全hhhh全 全hh全hh 全hh全全 全全hhhh 全全hh全 全全全hh 全全全全 /^(hh|全){2}h(hh|全)h$/ hhhhh全h hh全h全h 全hhh全h 全全h全h /^(hh|全)h(hh|全){2}h$/ hhh全hhh hhh全全h 全h全hhh 全h全全h /^h(hh|全){3}h$/ h全hhhhh h全hh全h h全全hhh h全全全h /^h(hh|全){2}h(hh|全)$/ hhh全h全 h全hhh全 h全全h全 /^h(hh|全)h(hh|全){2}$/ h全h全hh h全h全全
そうです。全角と半角が交互に来るなどのパターンを想定しなければなりません。うえの6個の正規表現を|で繋げれば全パターンを網羅しますが、それだけでもかなり冗長ですし、そえぞれのh
や全
に入る文字クラスは、先に挙げた文字クラスです。それだけでもかなり大きくなります。
これは8文字の場合ですが、40文字ともなると、組合せ爆発で莫大にパターンが増えます。現実的な大きさとは行かないでしょう。
本当に無理なのか
Rubyの正規表現であるonigmoは非常に強力です。全角半角ですが、マッチ可能なUnicodeプロパティとしてonigmoに追加するというプルリクがありますが、将来これが採用されれば、最初の文字クラスが大きい問題は解決できるかも知れません。あとは、パターンの網羅ですが、部分式呼び出しや条件分岐などを駆使すれば、もしかしたらもっと単純にできる可能性があります。ただ、私には思いつきませんでした。
投稿2019/07/06 01:07
総合スコア21741
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
両者を組み合わせれば、全角半角それぞれの文字数が出せるのではないでしょうか。
投稿2019/07/03 06:13
総合スコア13727
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
まともな正規表現ではたぶん書けないと思います。
(まともでない正規表現なら全角半角のすべての組み合わせを書いてorを取ればたぶんできますが)
またそもそも、全角半角で文字数に違いをつける意義がわかりません。
何のために全角半角で文字数に違いをつけたいのですか?
昔は幅やバイト数を揃える目的でそのような仕様がありましたが、今は普通はどちらも揃いません。
さらに、全角半角の区別は定義が様々あるのでまずどの定義でいくか決める必要があります。
投稿2019/07/02 17:01
総合スコア3047
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
ベストアンサー
例えばcharacter varying(40)の型のカラムに更新しようとした場合、
そもそも、https://www.postgresql.jp/document/11/html/datatype-character.html
これらのデータ型は2つともn文字長(バイト数ではなく)までの文字列を保存できます。
ですが、認識は合っていますでしょうか?
あと、バイト数だとしても、全角日本語が2バイトなのはShift_JISとeuc-jp(半角片仮名も2バイト)で、UTF-8だと3バイト以上です。
投稿2019/07/02 12:37
総合スコア86281
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。