[ Excel_Tipe29 へ戻る ]     ( Coloring  by  SyntaxHighlighter )

Tips29: JavaScript で電話番号(局番)の検証/編集    [ ソースファイル ダウンロード ]  [ Ruby 用 テストツール ]

  [ JavaScript with JSON  ( AreaCode4JSON.json ) ]   [ JavaScript ]   [ JAVA ]   [ PHP ]   [ Perl ]   [ Python ]   [ Ruby ]   [ VBA / VB6 ]   [ VB.net ]


  ( Source Code : Ruby   Ver 1.00 )    動作確認環境 [ Eclipse / Ruby 2.5.3 ]  番号翻訳テーブル [ 2022/7/1 版 ]

#! ruby -EUTF-8
# -*- mode:ruby; coding:utf-8 -*-


#//--【 Ruby 移植版 TelephoneCheck.rb (Ver 1.00 , 翻訳テーブル:2022/7/1 版) 】------------------------------
#//
#//  電話番号のハイフン検証/編集関数
#//  ( validate_telephone / format_telephone / get_type_telephone / phone_number )
#//
#// 【 移植元ソース:番号翻訳テーブル 内包版 TelephoneCheck.js 】
#//
#//   Tips29: JavaScriptで電話番号(局番)のハイフン検証/編集
#//   http://addinbox.sakura.ne.jp/Excel_Tips29.htm
#//
#//   Author : AddinBox(角田) http://addinbox.sakura.ne.jp/
#//
#//   β  1.00 , 2019/ 2/14 (JavaScript版 初版)
#//   β  4.40 , 2019/ 3/27 (JavaScript版)
#//   β  4.40 , 2019/ 3/27 (Ruby 移植)
#//   - - - - - - - - - - - -
#//   Ver 1.00 , 2019/ 4/ 4 (Ruby 移植, 番号翻訳テーブル:2019/4/1 版)
#//   Ver 1.00 , 2019/ 5/11 (Ruby 移植, 番号翻訳テーブル:2019/5/7 版)
#//   Ver 1.00 , 2020/ 7/20 (Ruby 移植, 番号翻訳テーブル:2020/6/1 版)
#//   Ver 1.00 , 2022/ 8/ 2 (Ruby 移植, 番号翻訳テーブル:2022/7/1 版)
#//
#//   *1 固定電話番号は番号翻訳テーブルによって、全国の市外局番を忠実に判定します。
#//      下記で翻訳テーブルの仕組みを確認できます。
#//          [ 電話番号翻訳テーブル 翻訳シュミレーター ]
#//            http://addinbox.sakura.ne.jp/Excel_Tips29.htm#S2_2
#//-------------------------------------------------------------------------------
#//   【 サポートしている電話番号 】
#//       固定電話       10桁(先頭 0) [ 0ABCDE-FGHJ ]
#//       携帯電話       11桁(先頭 070/080/090) [ 0x0-CDEF-GHJK , C:1-9 ] 補:060は実施未定
#//       着信課金       10桁(先頭 0120) [ 0120-DEF-GHJ ]
#//       着信課金       11桁(先頭 0800) [ 0800-DEF-GHJK ]
#//       IP電話       11桁(先頭 050)  [ 050-CDEF-GHJK , C:1-9 ]
#//       M2M         11桁(先頭 020x) [ 020-CDE-FGHJK , C:1-3,5-9 ]
#//       ポケベル       11桁(先頭 0204) [ 020-4DE-FGHJK ]
#//       FMC         11桁(先頭 0600) [ 0600-DEF-GHJK ]
#//       情報料代理徴収 10桁(先頭 0990) [ 0990-DEF-GHJ ]
#//       全国統一番号   10桁(先頭 0570) [ 0570-DEF-GHJ ]
#//-------------------------------------------------------------------------------

module TelephoneCheck

    # 番号翻訳テーブル(2022/7/1 版)
    #   固定電話の全国の局番(市外局番+市内局番)および携帯電話(070/080/090)等の局番を
    #   収録しているJSONファイル(AreaCode4JSON.json)を必要最小限の内容に編集しています。
    #   完全版のフォーマットは [ http://addinbox.sakura.ne.jp/Excel_Tips29.htm ] を参照の事。
    #   尚、このRuby用データはJSONファイル作成ツールで一緒に作成していますので、
    #  「配列データ」の同一性/正確性は保障します。
    #
    $area_code_array = [
          [0,1,22,41,42,54,69,70,81,114],          [0,-3,2,4,7,10,15,19,20,21],                      [-103,0,0,-4,-4,-4,3,0,0,0],
          [0,0,-4,-4,-4,-4,-4,-5,0,0],             [0,0,0,-4,-4,-4,-4,5,-4,6],                       [0,0,-5,0,-5,-4,-4,-5,-4,0],
          [0,0,-5,-4,-4,-4,-4,-5,-5,0],            [0,0,-4,-4,-4,8,9,0,0,0],                         [0,0,-4,-4,-4,0,-5,-5,0,0],
          [0,0,-4,-4,-4,-4,-5,0,0,0],              [0,0,-4,-4,11,12,13,-4,14,0],                     [0,0,-4,-4,-4,-4,-4,-5,-3,-4],
          [0,-3,-4,-4,-4,-4,-4,-3,-5,-4],          [0,0,-4,-4,-5,0,-4,-4,0,0],                       [0,0,-4,-4,-4,-4,-5,-5,-4,0],
          [0,0,-4,16,17,18,-4,-4,0,0],             [0,0,-5,0,-5,-5,-4,0,-4,-4],                      [0,0,-4,-4,-4,-4,-4,0,-5,0],
          [0,0,-4,-4,-5,-5,-5,0,-5,0],             [0,0,-4,-4,-4,-4,-4,-3,-4,-4],                    [0,0,-4,-4,-4,-4,-4,-4,-3,0],
          [0,-4,-4,-4,-4,-4,-3,-4,-4,-3],          [130,0,23,25,26,27,32,34,36,39],                  [-4,0,-3,24,-4,-4,-4,-3,-4,-4],
          [-3,0,-4,-4,-3,-3,-3,-3,-3,-3],          [0,0,0,-4,-4,-4,-3,-4,-4,0],                      [-4,-4,-4,-4,-4,-3,-4,-4,-4,-3],
          [-4,0,-3,-3,-4,28,29,30,31,-4],          [-3,-3,-3,-3,-3,-3,-3,-4,-4,-3],                  [-3,0,0,-4,-4,-4,-4,-4,-4,-4],
          [-3,-3,-4,-4,-4,-3,-3,-3,-3,-3],         [-3,0,-4,-4,-4,-4,-4,-4,-4,-4],                   [-4,-4,-3,-4,33,-4,-4,-4,-4,-4],
          [-3,0,-4,-4,-4,-4,-3,-3,-3,0],           [-4,0,-3,-3,-4,0,-4,-4,35,-4],                    [0,0,-4,-4,0,-4,-4,-4,-3,-3],
          [-4,0,-4,37,-4,-4,-3,-4,-4,38],          [-3,0,-4,-3,-3,-4,-4,0,-4,-4],                    [-3,0,0,0,0,-3,-4,-4,-4,-4],
          [0,-4,-3,40,-4,-4,-4,-4,-3,-4],          [-3,0,-4,-4,-4,-3,-3,0,-3,0],                     [0,0,0,-2,-2,-2,-2,0,0,0],
          [0,0,43,47,-3,-3,48,49,51,52],           [-2,0,44,-3,-3,-3,-3,-3,45,46],                   [-3,0,-4,-4,-4,-4,-4,-4,-4,-4],
          [-3,-3,-4,-4,-3,-3,-3,-4,-4,0],          [-2,-3,-2,-2,-2,-2,-2,-3,-3,-2],                  [0,0,-3,-3,-3,0,-4,0,-4,-4],
          [-4,0,-3,-4,-3,-4,-4,-4,-3,0],           [50,-2,0,-3,-3,-4,-4,-3,-4,-4],                   [-2,0,-4,-4,-4,-4,-4,-4,-4,-2],
          [-4,0,-3,0,-3,-3,-3,-3,-3,-3],           [0,0,-3,-4,-4,-4,0,0,0,53],                       [0,0,-5,0,-5,0,-5,0,-5,0],
          [131,0,-3,55,59,60,61,62,64,65],         [0,-4,-4,-4,-3,-3,-4,-4,-4,56],                   [0,0,-3,0,0,0,57,58,-3,0],
          [0,-4,-4,-4,-3,-3,-3,-3,-3,-3],          [0,0,0,-3,-4,0,0,-4,0,0],                         [0,0,-3,-3,-4,-4,-3,-4,-4,-3],
          [-4,-4,-3,-4,-4,-4,-4,-4,-4,-3],         [0,-4,-4,-4,-4,-4,-4,-4,-4,-4],                   [-110,0,-4,-4,-4,-4,63,-4,-4,0],
          [0,0,-4,-4,-4,-4,-4,-4,0,-5],            [0,-4,-3,-3,-4,-4,-4,-4,0,0],                     [0,0,-3,-3,-4,-4,-4,66,-4,68],
          [0,0,-4,-4,-4,0,0,-4,-4,67],             [0,0,-5,-5,-5,-5,-5,-4,-4,-5],                    [0,0,-4,-4,-4,-4,-4,-4,-4,-3],
          [132,0,0,0,-2,0,-2,-2,0,0],              [133,0,71,72,73,-3,75,76,-3,77],                  [0,-4,-3,-3,-3,-4,-3,-3,-3,-3],
          [0,0,0,0,-3,-4,-4,-4,-4,-4],             [-4,0,-4,-4,-4,-4,74,-4,-4,-4],                   [0,0,0,-4,-4,-4,-4,0,-5,0],
          [0,-4,-3,-4,-3,-4,-4,-4,-4,0],           [-4,-4,-4,-4,-4,-3,-4,0,-4,-4],                   [-4,-4,-3,-3,78,79,80,-4,-4,-4],
          [0,0,-3,-3,-3,-3,-4,-4,-4,-3],           [-3,0,-4,-4,-4,-3,-3,-4,-4,-3],                   [-3,0,-4,-4,-4,-4,-3,-3,-4,-4],
          [134,0,82,87,94,96,98,111,112,113],      [-4,0,-3,-4,83,-3,-4,-4,-3,84],                   [-3,0,-3,-3,-4,-4,-4,-4,-4,-3],
          [-3,0,85,-4,86,-4,-3,-4,-4,0],           [-4,-3,-3,-3,-3,-3,-3,-3,-3,-3],                  [-4,-3,-3,-3,-4,-4,0,0,0,0],
          [0,0,-3,-4,-4,-4,88,89,91,92],           [-3,0,-4,-4,-4,-4,-4,-4,-4,-4],                   [0,0,-4,-4,-4,-4,90,-3,-3,0],
          [-4,0,-4,-4,-4,-4,-3,-3,-3,-4],          [0,0,-4,-4,-4,-4,0,-5,-5,0],                      [-3,0,-3,-3,-3,-3,93,-3,-3,-3],
          [0,0,-5,-3,0,-5,-3,-5,-5,0],             [0,0,0,0,0,-4,-4,95,-4,-3],                       [0,0,-4,-4,-4,-4,-4,-5,-4,0],
          [0,97,-4,-4,-4,-4,-4,-4,-4,-4],          [0,0,-5,0,-5,0,0,0,0,0],                          [0,0,-3,99,-3,101,103,105,108,109],
          [0,0,-4,-4,-4,-4,100,-4,-4,0],           [0,0,-3,-3,-3,0,-4,0,-3,0],                       [0,0,-3,0,-4,102,-4,-4,0,0],
          [-4,0,-3,0,-4,0,0,0,-4,0],               [-3,0,-4,-4,-4,-4,-4,-4,-4,104],                  [-4,-3,-4,-4,-4,-4,-4,-3,-3,-4],
          [0,0,106,107,-4,-4,-4,-4,-4,-4],         [0,0,-3,-3,-3,-4,-3,-4,-3,0],                     [0,0,0,-4,-4,-4,-4,-3,-3,0],
          [-3,0,-4,-4,-4,-4,-4,-4,-4,-3],          [-3,0,-4,-4,-3,-3,-4,-4,-4,110],                  [0,0,-4,-4,0,-3,0,-3,0,-3],
          [0,0,0,0,0,-4,0,-4,-3,-4],               [-4,0,0,-4,-4,-4,-3,-4,-3,-4],                    [0,0,-4,-4,-4,-4,-4,-4,-4,-3],
          [135,0,115,116,117,118,119,120,121,123], [-4,0,-3,-3,-3,-3,-3,-3,-3,-3],                   [-4,0,-3,-3,-3,-3,-3,-3,-3,-3],
          [-4,0,-4,-4,-4,0,-4,-4,-4,-4],           [-4,0,-4,0,-4,-4,-4,-4,-3,-4],                    [0,0,-3,-3,-4,-4,-4,-4,-4,-4],
          [0,0,-4,-4,-4,-3,0,-4,-4,-4],            [122,0,-4,-4,-4,-4,-4,-4,-3,-3],                  [0,0,-5,-4,-4,-4,-4,-4,-4,-4],
          [-109,124,-3,125,128,-4,129,-4,-3,0],    [0,0,-5,-5,0,0,0,0,0,0],                          [0,0,-4,126,127,-4,-4,-4,-4,0],
          [0,-3,-4,-4,-4,-4,-4,-4,-4,-4],          [-4,-4,0,-3,0,-3,0,-3,0,-4],                      [-3,0,-4,-4,-4,-4,-4,-3,-3,-4],
          [0,0,-4,-4,-4,-4,-4,-4,-4,-5],           [0,-106,-106,-106,-107,-106,-106,-106,-106,-106], [0,-105,-105,-105,-105,-105,-105,-105,-105,-105],
          [-108,0,0,0,0,0,0,0,0,0],                [0,-102,-102,-102,-102,-102,-102,-102,-102,-102], [-104,-102,-102,-102,-102,-102,-102,-102,-102,-102],
          [0,-102,-102,-102,-102,-102,-102,-102,-102,-102]
    ]


    #//-------------------------------------------------------------------------------
    #// 電話番号のフォーマット&検証 ( phone_number )
    #//    --- validate_telephone/format_telephone の簡易インターフェース ---
    #//
    #// TelCode -- 半角文字列[ 0-9, -, (, ) ]
    #//            固定電話/携帯電話などの[ハイフン/括弧]編集 or 未編集電話番号
    #//
    #// Action  -- "V"        : 電話番号の検証
    #//            "F1"〜"F6" : 電話番号の区切り編集( 2桁目が EditType の値 )
    #//
    #// 返却値
    #//    Action= "V"        -- 0: 検証エラー , 1:検証OK , 2:検証OK(電話番号が数字のみ)
    #//                            (Boolean 変換すると 0⇒False, 1/2⇒True になります)
    #//                          (*) Ruby版では[ 0 ]の代わりに[ false ]を返します
    #//
    #//          = "F1"〜"F6" -- 電話番号が正しい場合: EditType(2桁目)に従って編集した電話番号
    #//                          電話番号が誤っている場合: "Phone Error"
    #//
    #//          上記以外     -- "" (Boolean変換でFalse)で返ります。
    #//                          (*) Ruby版では[ "" ]の代わりに[ false ]を返します
    #// 
    #//-------------------------------------------------------------------------------
    def self.phone_number(tel_code = "", action = "")

        # format_telephone/validate_telephone の返却値
        result = init_telephone("Value")

        str_action = ""
        pattern_action = /^(([Ff][1-6])|[Vv])$/;  # "F1"〜"F6" or "V"

        if (action.is_a?(String))
            if (action =~ pattern_action)
                str_action = action.upcase
            else
                return false
            end
        else
             return false
        end

        if (! tel_code.is_a?(String) )
            return false
        end

        if (str_action == "V")
            result = validate_telephone(tel_code)
            case (result[:ErrId])
                when 0
                    return 1      # OK(Boolean変換でTrue)
                when 8
                    return 2      # OK (電話番号が数字のみ, Boolean変換でTrue)
                else
                    return false  # エラー(Boolean変換でFalse) , Ruby版では[0]の代わりにfalse
            end
        else
            result = format_telephone(tel_code, str_action[1, 1].to_i )
            if (result[:ErrId] == 0)
                return result[:TelEdit]
            else
                return "Phone Error"
            end
        end
    end


    #//-------------------------------------------------------------------------------
    #// 返却値を受け取る変数に連想配列の初期設定 ( init_telephone )
    #// 
    #// target -- "Value" or "Type" (左記以外 "Value" 扱い)
    #//
    #// 返却値 -- target = "Value" の場合、下記の連想配列
    #//              {TelType: -1, EditType: 9, TelEdit: '', ErrId: 0}
    #//           target = "Type" の場合、下記の連想配列
    #//              {'TelType':-1, 'SizeAll':0, 'Size1':0, 'Size2':0, 'Size3':0, 'ErrId':0}
    #//-------------------------------------------------------------------------------
    def self.init_telephone(target = 'Value')

        target2 = ''

        if (target.is_a?(String))
            if (target.upcase == 'VALUE')
                target2 = 'Value'
            elsif (target.upcase == 'TYPE')
                target2 = 'Type'
            end
        else
            target2 = 'Value'
        end

        if (target2 == 'Value')
            return {TelType: -1, EditType: 9, TelEdit: '', ErrId: 0}
        else
            return {TelType: -1, SizeAll: 0, Size1: 0, Size2: 0, Size3: 0, ErrId: 0}
        end
    end


    #//-------------------------------------------------------------------------------
    #// 電話番号の検証 ( validate_telephone )
    #//
    #// tel_code -- 半角文字列[ 0-9, -, (, ) ]
    #//             固定電話/携帯電話などの[ハイフン/括弧]編集
    #//
    #// 返却値  -- {TelType, EditType, TelEdit, ErrId} のハッシュ(Keyは大文字混じり)
    #//   TelType   1:固定電話(10桁), 2:携帯電話(070/080/090,11桁), 3:着信課金(0120,10桁),
    #//             4:着信課金(0800,11桁), 5:IP電話(050,11桁), 6:M2M(020x,11桁,x≠4),
    #//             7:ポケベル(0204,11桁), 8:FMC(0600,11桁), 9:情報料徴収(0990,10桁),
    #//             10:統一番号(0570,10桁), -1:other
    #//
    #//   EditType  1or4: 0AB-CDE-FGHJ, 2or5: 0AB(CDE)FGHJ, 3or6: (0AB)CDE-FGHJ, 9:other
    #//             携帯電話のみ、[3-4-4 桁]で区切る⇒1〜3 , [3-3-5 桁]で区切る⇒4〜6
    #//
    #//   TelEdit   TelType/EditType で編集し直された電話番号(正しい編集結果)
    #//               ErrId=0/1/3/5 : 同じパターンで編集し直された電話番号
    #//                 〃 =8       : ハイフン編集した電話番号
    #//                 〃 =2       : TelCode のまま(未使用局番なので正誤は判断しない)
    #//                 〃 =4/6/9   : 空文字
    #//
    #//   ErrId     0:OK, 1:区切り位置不正, 2:未使用局番(固定電話), 3:市内局番1桁目が[0 or 1](固定電話),
    #//             4:引数不正([数字,ハイフン,括弧]以外がある or 編集パターン不正 or '0'始まりでない or 桁数不足),
    #//             5:引数不正(局番タイプに応じた桁数と不一致),
    #//             6:その他のエラー, 8:OK(数字のみ指定), 9:引数未定義(TelCode)
    #//-------------------------------------------------------------------------------

    def self.validate_telephone(tel_code = '')

        # 返却値
        result = init_telephone('Value')
        # format_telephone の返却値
        result_format = init_telephone('Value')
        # 携帯電話(3-3-5桁区切り編集の再取得用)
        result_format2 = init_telephone('Value')

        str_tel_code = ''  # Rubyには引数に型指定が無いのでチェック&変換する
        tel_number = ''    # ハイフン,括弧除去(数字のみ)
        edit_type = 9

        # 厳密なチェックは format_telephone の結果と比較して行なうので
        # 下記のパターンチェックでは桁数については曖昧で構わない。
        pattern0 = /^0\d+$/               # 0123456789
        pattern1 = /^0\d+-\d+-\d+$/       # 012-345-6789
        pattern2 = /^0\d+\(\d+\)\d+$/     # 012(345)6789
        pattern3 = /^\(0\d+\)\d+-\d+$/    # (012)345-6789
        remove_pattern = /-|\(|\)/        # ハイフン,括弧の除去用

        if (tel_code.is_a?(String))
            str_tel_code = tel_code
        elsif (tel_code.is_a?(Integer))
            str_tel_code = tel_code.to_s    # 数値の場合、先頭'0'が取れるので次の判定でエラーで返る
        else
            result[:ErrId] = 9
            return result
        end

        if (str_tel_code =~ pattern0)  # (数字のみ)  0123456789
            # 数字のみ指定 ⇒ 編集のみ行って比較検証は無し
            result_format = format_telephone(str_tel_code, 1)  # ハイフン編集で固定
            result[:TelType] = result_format[:TelType]
            result[:EditType] = 1
            result[:TelEdit] = result_format[:TelEdit]
            # validate用のエラーコードに差替え
            case result_format[:ErrId]
            when 0
                result[:ErrId] = 8         # OK(数字のみ指定)
            when 1
                result[:ErrId] = 2         # 未使用局番(固定電話番号)
            when 2
                result[:ErrId] = 3         # 市内局番1桁目が[0 or 1](固定電話)
            when 3
                result[:ErrId] = 4         # 桁数不足(数字が10桁未満)
            when 4
                result[:ErrId] = 5         # 局番タイプに応じた桁数と不一致
            else
                result[:ErrId] = 6         # その他のエラー
            end
            return result
        elsif (str_tel_code =~ pattern1)   # edit_type:1  012-345-6789
            edit_type = 1
            result[:EditType] = 1
        elsif (str_tel_code =~ pattern2)   # edit_type:2  012(345)6789
            edit_type = 2
            result[:EditType] = 2
        elsif (str_tel_code =~ pattern3)   # edit_type:3  (012)345-6789
            edit_type = 3
            result[:EditType] = 3
        else
            # [数字,ハイフン,括弧]以外がある or 編集パターン不正 or '0'始まりでない
            result[:ErrId] = 4
            result[:TelEdit] = str_tel_code
            return result
        end

        # format_telephone 用に[ハイフン,括弧]を除去して「数字のみ」にする
        tel_number = str_tel_code.gsub(remove_pattern, '')    # ハイフン,括弧の除去

        result_format = format_telephone(tel_number, edit_type)
        # format_telephone で編集し直した結果と比較して検証する
        # ErrId で返るのは
        #    0:OK, 1:未使用局番(固定電話), 2:市内局番1桁目が[0 or 1](固定電話)
        #    3:引数不正(TelCode: 数字が10桁未満),
        #    4:引数不正(TelCode: 局番タイプの桁数と不一致)
        # 下記状態は既にチェック済なので、その値が返ることは無い
        #    3:引数不正(TelCode: ['0'始まりの数字]以外)
        #    6:引数不正(EditType)
        #    9:引数未定義(TelCode/EditType)

        result[:TelType] = result_format[:TelType]
        result[:TelEdit] = result_format[:TelEdit]
        # validate用のエラーコードに差替え
        case result_format[:ErrId]
        when 0  # format_telephoneでOK ⇒ 正しい電話番号
            if (str_tel_code == result_format[:TelEdit])
                result[:ErrId] = 0    # OK (区切り位置も正しい)
            else
                if (result_format[:TelType] == 2)    # 携帯電話(070/080/090)
                    # 携帯電話の場合、別の区切り方[3-3-5 桁]で再チェック
                    result_format2 = format_telephone(tel_number, edit_type + 3)
                    if (str_tel_code == result_format2[:TelEdit])
                        # OK (3-3-5桁で一致)
                        result[:ErrId] = 0
                        result[:TelEdit] = result_format2[:TelEdit]
                        result[:EditType] = edit_type + 3
                    else
                        result[:ErrId] = 1    # 区切り位置不正
                    end
                else
                    # 携帯電話 以外
                    result[:ErrId] = 1    # 区切り位置不正
                end
            end
        when 1
            result[:ErrId] = 2                 # 未使用局番(固定電話番号)
            result[:TelEdit] = str_tel_code    # ResultFormat.TelEdit(=TelNumber)なので入力値に差し替える
        when 2
            result[:ErrId] = 3            # 市内局番1桁目が[0 or 1](固定電話)
        when 3
            result[:ErrId] = 4            # 桁数不足(数字が10桁未満)
        when 4
            result[:ErrId] = 5            # 局番タイプに応じた桁数と不一致
        else
            result[:ErrId] = 6            #その他のエラー
        end

        return result
    end


    #//-------------------------------------------------------------------------------
    #// 電話番号のハイフン編集 ( format_telephone )
    #//
    #// tel_code  -- 半角数字文字列[ 0-9 ], 10 or 11桁(先頭'0'固定)
    #//              固定電話/携帯電話などの電話番号
    #//
    #// edit_type -- 数値
    #//              1or4: 0AB-CDE-FGHJ , 2or5: 0AB(CDE)FGHJ , 3or6: (0AB)CDE-FGHJ
    #//              携帯電話のみ、[3-4-4 桁]で区切る⇒1〜3 , [3-3-5 桁]で区切る⇒4〜6
    #//
    #// 返却値  -- {TelType, EditType, TelEdit, ErrId} のハッシュ(Keyは大文字混じり)
    #//   TelType   1:固定電話(10桁), 2:携帯電話(070/080/090,11桁), 3:着信課金(0120,10桁),
    #//             4:着信課金(0800,11桁), 5:IP電話(050,11桁), 6:M2M(020x,11桁,x≠4),
    #//             7:ポケベル(0204,11桁), 8:FMC(0600,11桁), 9:情報料徴収(0990,10桁),
    #//             10:統一番号(0570,10桁), -1:other
    #//
    #//   EditType  1or4: 0AB-CDE-FGHJ, 2or5: 0AB(CDE)FGHJ, 3or6: (0AB)CDE-FGHJ, 9:other
    #//
    #//   TelEdit   編集された電話番号
    #//               ErrId=0/2/4 : 編集された電話番号
    #//                〃  =1     : TelCode の内容のまま
    #//                〃  =3/6/9 : 空文字
    #//
    #//   ErrId     0:OK, 1:未使用局番(固定電話), 2:市内局番1桁目が[0 or 1](固定電話),
    #//             3:引数不正(TelCode: ['0'始まりの数字]以外 or 10桁未満),
    #//             4:引数不正(TelCode: 局番タイプに応じた桁数と不一致)
    #//             6:引数不正(EditType), 9:引数未定義(TelCode/EditType)
    #//-------------------------------------------------------------------------------

    def self.format_telephone(tel_code = '', edit_type = 0)

        # 返却値
        result = init_telephone('Value')
        # get_type_telephone 返却値
        result_Type = init_telephone('Type')

        str_tel_code =''   # Rubyには引数に型指定が無いのでチェック&変換する
        int_edit_type = 0

        tel_pattern  = /^0\d{9,}$/        # '0'始まりの数字文字列(10桁以上)
        edit_type_pattern = /^[1-6]$/    # '1' 〜 '6'(1文字)


        if (tel_code.is_a?(String))
            str_tel_code = tel_code
        elsif (tel_code.is_a?(Integer))
            str_tel_code = tel_code.to_s    # 数値の場合、先頭'0'が取れるので次の判定でエラーで返る
        else
            result[:ErrId] = 9
            return result
        end

        # ['0'始まりの数字文字列(10桁以上)]のチェック
        # (番号翻訳(get_type_telephone)後に完全な桁数チェックを行なう。ここでは10桁未満のみNG)
        if (str_tel_code !~ tel_pattern)
            result[:ErrId] = 3
            return result
        end

        if (edit_type.is_a?(String))
            if (edit_type =~ edit_type_pattern)
                int_edit_type = edit_type.to_i    # OK
            else
                result[:ErrId] = 6
                return result
            end
        elsif (edit_type.is_a?(Integer))
            if ((edit_type >= 1) and (edit_type <= 6))
                int_edit_type = edit_type    # OK
            else
                result[:ErrId] = 6
                return result
            end
        else
            result[:ErrId] = 6
            return result
        end
        result[:EditType] = int_edit_type

        result_type = get_type_telephone(str_tel_code)   # 番号翻訳して電話番号の種別と桁区切り情報を取得する
        # ErrId で返るのは
        #    0:OK, 1:未使用局番(固定電話), 2:市内局番1桁目が[0 or 1](固定電話)
        # 下記状態は既にチェック済なので、その値が返ることは無い
        #    3:電話番号種別が不明(桁数不足で番号翻訳未了)← 10桁以上なので未了は無い
        #    8:引数不正(TelCode: ['0'始まりの数字]以外)
        #    9:引数未定義(TelCode)

        result[:TelType] = result_type[:TelType]
        result[:ErrId] = result_type[:ErrId]
        if (result_type[:ErrId] == 1)
            # 未使用局番(固定電話)
            result[:TelEdit] = str_tel_code
        else
            # 0:OK  or  2:市内局番1桁目が[0 or 1](固定電話)
            if (result_type[:TelType] == 2 && int_edit_type >= 4)
                # 携帯電話(070/080/090)で[3-3-5 桁区切り, 0x0-CDE-FGHJK]を指定された場合
                result[:TelEdit] = insert_separator(str_tel_code, int_edit_type, 3, 3)
            else
                # 携帯電話(3-4-4桁区切り)を含め、他は返却値で区切りパターンが得られる
                # 固定電話では[市外局番桁数/市内局番桁数/加入者番号桁数(4)]が得られる
                if (result_type[:Size2] == 0)
                    # 固定電話で、市内局番なし [ 0ABCDE-FGHJ ] (現在、この地域は存在しない)
                    result[:TelEdit] = str_tel_code[0 , 6] + '-' + str_tel_code[6 , (str_tel_code.length - 6)]
                else
                    result[:TelEdit] = insert_separator(str_tel_code, int_edit_type, result_type[:Size1], result_type[:Size2])
                end
            end

            if (str_tel_code.length != result_type[:SizeAll])
                result[:ErrId] = 4    # 桁数エラー(局番タイプに応じた桁数と不一致)
            end
        end

        return result
    end


    def self.insert_separator(tel_code, edit_type, size1, size2)
        result = ''
        remain_size = tel_code.length - (size1 + size2)
        edit_code1 = tel_code[0 , size1]                    # 1st section :固定電話では市外局番(先頭'0')
        edit_code2 = tel_code[size1 , size2]                # 2nd section :固定電話では市内局番
        edit_code3 = tel_code[size1 + size2 , remain_size]  # 3rd section :固定電話では加入者番号(末尾まで取り出す)

        case edit_type
        when 1, 4
            # 0AB-CDE-FGHJ
            result = edit_code1 + '-' + edit_code2 + '-' + edit_code3
        when 2, 5
            # 0AB(CDE)FGHJ
            result = edit_code1 + '(' + edit_code2 + ')' + edit_code3
        when 3, 6
            # (0AB)CDE-FGHJ
            result = '(' + edit_code1 + ')' + edit_code2 + '-' + edit_code3
        end

        return result
    end



    #//-------------------------------------------------------------------------------
    #// 電話番号タイプの取得 ( get_type_telephone )
    #//
    #// tel_code  -- 半角数字文字列(2桁以上, 先頭'0'固定)
    #//             固定電話/携帯電話などの電話番号(途中までの内容でも可)
    #//
    #// 返却値 -- {TelType, SizeAll, Size1, Size2, Size3, ErrId} のハッシュ(Keyは大文字混じり)
    #//   TelType   1:固定電話(10桁), 2:携帯電話(070/080/090,11桁), 3:着信課金(0120,10桁),
    #//             4:着信課金(0800,11桁), 5:IP電話(050,11桁), 6:M2M(020x,11桁,x≠4),
    #//             7:ポケベル(0204,11桁), 8:FMC(0600,11桁), 9:情報料徴収(0990,10桁),
    #//             10:統一番号(0570,10桁), -1:other
    #//
    #//   SizeAll   ErrId=0/2 : TelType に応じた電話番号の桁数(10 or 11, Size1〜3の合計値)
    #//             ErrId=1/3/8/9 : 0
    #//
    #//   Size1-3   ErrId=0/2 : TelType に応じた電話番号の区切り桁数(Size1 には先頭'0'含む)
    #//             ErrId=1/3/8/9 : 0
    #//             (携帯電話(TelType=2)は[3-4-4 桁区切り]のみで返す)
    #//
    #//   ErrId     0:OK, 1:未使用局番(固定電話), 2:市内局番1桁目が[0 or 1](固定電話),
    #//             3:電話番号種別が不明(桁数不足で番号翻訳未了)
    #//             8:引数不正(TelCode: ['0'始まりの数字]以外), 9:引数未定義(TelCode)
    #//-------------------------------------------------------------------------------

    def self.get_type_telephone(tel_code = '')

        # 返却値
        result = init_telephone('Type')

        str_tel_code = ''  # Rubyには引数に型指定が無いので空文字を連結して強制的に文字列型にする

        area_code_length = 0
        city_code_length = 0
        city_code1 = ''      # 市内局番の1桁目
        idx = 0
        cnt = 0
        num = 0
        link_value = 0
        tel_type = 0
        tel_pattern  = /^0\d+$/    # '0'始まりの数字文字列(桁数制限なし)

        # ( 1)固定電話       10桁 [ 桁区切りは番号翻訳結果に拠る ]
        # ( 2)携帯電話       11桁 [ 0x0-CDEF-GHJK , 070/080/090 ]
        # ( 3)着信課金       10桁 [ 0120-DEF-GHJ  ]
        # ( 4)着信課金       11桁 [ 0800-DEF-GHJK ]
        # ( 5)IP電話       11桁 [ 050-CDEF-GHJK ]
        # ( 6)M2M         11桁 [ 020-CDE-FGHJK ]
        # ( 7)ポケベル       11桁 [ 020-4DE-FGHJK ]
        # ( 8)FMC         11桁 [ 0600-DEF-GHJK ]
        # ( 9)情報料代理徴収 10桁 [ 0990-DEF-GHJ  ]
        # (10)全国統一番号   10桁 [ 0570-DEF-GHJ  ]
        #  SizeInfo [SizeAll, Size1, Size2, Size3]
        size_info = [
               [0,0,0,0],  [10,0,0,0], [11,3,4,4], [10,4,3,3], [11,4,3,4],
               [11,3,4,4], [11,3,3,5], [11,3,3,5], [11,4,3,4], [10,4,3,3], [10,4,3,3]
            ]

        if (tel_code.is_a?(String))
            str_tel_code = tel_code
        elsif (tel_code.is_a?(Integer))
            str_tel_code = tel_code.to_s    # 数値の場合、先頭'0'が取れるので次の判定でエラーで返る
        else
            result[:ErrId] = 9
            return result
        end

        # ['0'始まりの数字文字列(桁数制限なし)]のチェック
        # (桁数オーバーチェックは番号翻訳後に行なう)
        if (str_tel_code !~ tel_pattern)
            result[:ErrId] = 8
            return result
        end

        # ※ 番号翻訳テーブルをソースコード内に直に収録しているので
        #    JSONファイルの読み込み処理は必要ない。

        # 番号翻訳テーブルから局番情報(市外局番の桁数 or [携帯番号 等]のTelType情報)を取得する。
        # [tel_code]の長さが[局番桁数 未満]も受け入れているので『番号翻訳 途中』でのループ終了(tel_type= -1)もある。
        idx = 0
        tel_type = -1
        area_code_length = 0
        for cnt in 1 .. [5, str_tel_code.length].min    # 番号翻訳テーブルはA〜Eコードの5段(cnt=1〜5)
            num = str_tel_code[cnt, 1].to_i    # 電話番号の2桁目以降(1桁目は'0')を順次取り出す
            link_value = $area_code_array[idx][num]
            if (link_value == 0)   # 番号翻訳 終了(固定電話 未使用局番)
                tel_type = 1
                area_code_length = 0
                break
            elsif (link_value < 0)    # 番号翻訳 終了
                if (link_value > -100)
                    # 固定電話の市外局番桁数(符号反転値) 実際の収録値[ -2 〜 -5 ]
                    tel_type = 1
                    area_code_length = link_value * (-1)  # 市外局番 桁数決定(先頭の "0" 含む)
                else
                    #  携帯電話 等のtel_type値(符号反転値 - 100) 実際の収録値[ -102 〜 -110 ]
                    tel_type = (link_value * (-1)) - 100
                end
                break
            else  # プラス値
                idx = link_value  # 検索継続(収録値は NextIndex)
            end
        end

        # tel_type に応じた桁区切りを設定する
        # 固定電話(tel_type=1)では市外局番桁数(area_code_length)に従って設定する
        # Size1 の値は[先頭'0'含む]
        result[:TelType] = tel_type
        if (tel_type == -1)
            # 電話番号種別が不明(桁数不足で番号翻訳未了)
            result[:ErrId] = 3
        elsif (tel_type == 1)
            # 固定電話
            if (area_code_length == 0)
                # 未使用 局番
                result[:ErrId] = 1
            elsif (area_code_length == 6)
                # 市内局番なし [ 0ABCDE-FGHJ ] (現在、この地域は存在しない)
                result[:ErrId] = 0
                result[:SizeAll] = 10
                result[:Size1] = 6
                result[:Size2] = 0
                result[:Size3] = 4
            else
                city_code_length = 6 - area_code_length   # 10 - area_code_length - 4(加入者番号)
                if (str_tel_code.length < (area_code_length + 1))
                    # [TelCode]が市内局番の桁位置までないので市内局番適否の判定不可⇒ OK で返す
                    result[:ErrId] = 0
                else
                    # format_telephone から呼ばれる場合は 10桁以上なので必ず判定可能
                    city_code1 = str_tel_code[area_code_length , 1]  # 市内局番 1桁目
                    if (city_code1 == '0' or city_code1 == '1')
                        result[:ErrId] = 2    # 市内局番1桁目に0/1は割り当てられない
                    else
                        result['ErrId'] = 0    # OK
                    end
                end
                result[:SizeAll] = 10
                result[:Size1] = area_code_length
                result[:Size2] = city_code_length
                result[:Size3] = 4
            end
        else
            # tel_type = 2〜10, size_infoテーブルから桁情報を設定する
            result[:ErrId]   = 0    #/ OK
            result[:SizeAll] = size_info[tel_type][0]
            result[:Size1]   = size_info[tel_type][1]
            result[:Size2]   = size_info[tel_type][2]
            result[:Size3]   = size_info[tel_type][3]
        end

        return result
    end

end  # of module