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

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

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

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

正規表現

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

Q&A

解決済

7回答

5918閲覧

正規表現で、全半角混合の文字数(バイト数?)が規定値を超えていないかを判別する方法

hiepita1

総合スコア37

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

正規表現

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

0グッド

0クリップ

投稿2019/07/02 11:24

編集2019/07/03 01:36

前提・実現したいこと

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ページで確認できます。

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

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

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

guest

回答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
winterboum

総合スコア23329

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

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

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
think49

総合スコア18162

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

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

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

raccy

総合スコア21735

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

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

0

両者を組み合わせれば、全角半角それぞれの文字数が出せるのではないでしょうか。

投稿2019/07/03 06:13

KojiDoi

総合スコア13671

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

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

0

まともな正規表現ではたぶん書けないと思います。
(まともでない正規表現なら全角半角のすべての組み合わせを書いてorを取ればたぶんできますが)

またそもそも、全角半角で文字数に違いをつける意義がわかりません。
何のために全角半角で文字数に違いをつけたいのですか?
昔は幅やバイト数を揃える目的でそのような仕様がありましたが、今は普通はどちらも揃いません。

さらに、全角半角の区別は定義が様々あるのでまずどの定義でいくか決める必要があります。

投稿2019/07/02 17:01

ikadzuchi

総合スコア3047

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

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

hiepita1

2019/07/03 01:38

- 昔からのシステムの改修のため、こういった仕様になっています。 - 定義は明確には決まっていないので、全角2文字、半角1文字というシンプルな定義で大丈夫です。(サロゲートペアなどは一旦考慮しません。)
ikadzuchi

2019/07/05 18:43

なるほど改修ですか。そうなると、「定義は明確には決まっていない」ではなく、旧システムと全く同一の定義にすべきではないですか? 一方、旧システムから変えてよいのであれば、回答で述べたとおり全角半角を区別する意義はありませんのでこれを機に区別なしにするのも手です。
hiepita1

2019/07/08 01:14

旧システムで区別していた意義を確かめたいと思います。ありがとうございました。
guest

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

otn

総合スコア84529

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

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

hiepita1

2019/07/03 01:40

- はい、仕様が一般的でないことは、重々承知です。DB的には全半角区別せずに40文字まで更新可能なのが普通ですが、ここでは区別したいと考えています。
otn

2019/07/03 06:41 編集

ああ、データ連係している別システムがあって、そこの制約ですか。 では、データベースの話はあまり関係無くて、単に半角文字数、それ以外の文字数を知りたいと言う事か。
otn

2019/07/03 06:43

jsonschemaの機能では無理(一つの正規表現では現実的に無理)だと思いますので、Rubyのコードで判断するしかないかと。
guest

0

  • 皆様ご丁寧な回答ありがとうございました。 jsonschemaでの正規表現で行うのは難しいということを理解できました。
  • 無理に正規表現でやった場合には、現実的ではない量の正規表現を書くことになること。もしもrubyでやる場合のサンプルコード例など、多くの有用な情報が得られました。

投稿2019/07/08 01:17

hiepita1

総合スコア37

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問