タグ : 文字化け

 

.NET 文字列がJIS X 0208 漢字第二水準までで構成されているかをチェックする

C#、VB.NETで文字列がJIS X 0208 漢字第二水準までで構成されていることをチェックします。
 
最近のWEBサイトは文字コードをUnicodeで作成するため、文字化けを考慮することは少なくなってきましたが、一昔前までは文字列をWindowsとMac両方で文字化けなく表示させるため、入力できる文字に制限を付けることが一般的でした。
 
その際よく用いられるのが「JIS漢字第二水準まで許可」という仕様です。これは『半角英数字記号、全角記号・特殊文字、数字、ラテン文字、ひらがな、カタカナ、ギリシャ文字、キリル文字、罫線素片、漢字第1水準、漢字第2水準』で構成された文字列かどうかを判定します。
 
現在でも基幹系システムとの連携やラベルプリンタへの出力などで、丸付き数字やはしご高が文字化けする例が散見されますので、チェックのやり方は知っておくべきです。
 
●文字列がJIS X 0208 漢字第二水準までで構成されているかをチェック
        /// <summary>
        /// 文字列が半角英数字記号かどうかを判定します
        /// </summary>
        /// <param name="target">対象の文字列</param>
        /// <returns>文字列が半角英数字記号の場合はtrue、それ以外はfalse</returns>
        public static bool IsASCII(string target)
        {
            return new Regex("^[\x20-\x7E]+$").IsMatch(target);
        }

        /// <summary>
        /// 文字列が半角カタカナ(句読点~半濁点)かどうかを判定します
        /// </summary>
        /// <param name="target">対象の文字列</param>
        /// <returns>文字列が半角カタカナ(句読点~半濁点)の場合はtrue、それ以外はfalse</returns>
        public static bool IsHalfKatakanaPunctuation(string target)
        {
            return new Regex("^[\uFF61-\uFF9F]+$").IsMatch(target);
        }

        /// <summary>
        /// 文字列がJIS X 0208 漢字第二水準までで構成されているかを判定します
        /// </summary>
        /// <param name="target">対象の文字列</param>
        /// <param name="containsHalfKatakana">漢字第二水準までに半角カタカナを含む場合はtrue、それ以外はfalse</param>
        /// <returns>文字列がJIS X 0208 漢字第二水準までで構成されている場合はtrue、それ以外はfalse</returns>
        public static bool IsUntilJISKanjiLevel2(string target, bool containsHalfKatakana)
        {
            // 文字エンコーディングに「iso-2022-jp」を指定
            Encoding encoding = Encoding.GetEncoding("iso-2022-jp");

            // 文字列長を取得
            int length = target.Length;

            for (int i = 0; i < length; i++)
            {
                // 対象の部分文字列を取得
                string targetSubString = target.Substring(i, 1);

                // 半角英数字記号の場合
                if (IsASCII(targetSubString) == true)
                {
                    continue;
                }

                // 漢字第二水準までに半角カタカナを含まずかつ対象の部分文字列が半角カタカナの場合
                if (containsHalfKatakana == false &&
                    IsHalfKatakanaPunctuation(targetSubString) == true)
                {
                    return false;
                }

                // 対象部分文字列の文字コードバイト配列を取得
                byte[] targetBytes = encoding.GetBytes(targetSubString);

                // 要素数が「1」の場合は漢字第三水準以降の漢字が「?」に変換された
                if (targetBytes.Length == 1)
                {
                    return false;
                }

                // 文字コードバイト配列がJIS X 0208 漢字第二水準外の場合
                if (IsUntilJISKanjiLevel2(targetBytes) == false)
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 文字列がJIS X 0208 漢字第二水準までで構成されているかを判定します
        /// </summary>
        /// <param name="target">対象の文字列</param>
        /// <returns>文字列がJIS X 0208 漢字第二水準までで構成されている場合はtrue、それ以外はfalse</returns>
        /// <remarks>句読点~半濁点の半角カタカナはJIS X 0208 漢字第二水準外と判定します</remarks>
        public static bool IsUntilJISKanjiLevel2(string target)
        {
            return IsUntilJISKanjiLevel2(target, false);
        }

        /// <summary>
        /// 文字コードバイト配列がJIS X 0208 漢字第二水準までであるかを判定します
        /// </summary>
        /// <param name="targetBytes">文字コードバイト配列</param>
        /// <returns>文字コードバイト配列がJIS X 0208 漢字第二水準までである場合はtrue、それ以外はfalse</returns>
        private static bool IsUntilJISKanjiLevel2(byte[] targetBytes)
        {
            // 文字コードバイト配列の要素数が8ではない場合
            if (targetBytes.Length != 8)
            {
                return false;
            }

            // 区を取得
            int row = targetBytes[3] - 0×20;

            // 点を取得
            int cell = targetBytes[4] - 0×20;

            switch (row)
            {
                case 1: // 1区の場合
                    if (1 <= cell && cell <= 94)
                    {
                        // 1点~94点の場合
                        return true;
                    }

                    break;

                case 2: // 2区の場合
                    if (1 <= cell && cell <= 14)
                    {
                        // 1点~14点の場合
                        return true;
                    }
                    else if (26 <= cell && cell <= 33)
                    {
                        // 26点~33点の場合
                        return true;
                    }
                    else if (42 <= cell && cell <= 48)
                    {
                        // 42点~48点の場合
                        return true;
                    }
                    else if (60 <= cell && cell <= 74)
                    {
                        // 60点~74点の場合
                        return true;
                    }
                    else if (82 <= cell && cell <= 89)
                    {
                        // 82点~89点の場合
                        return true;
                    }
                    else if (cell == 94)
                    {
                        // 94点の場合
                        return true;
                    }

                    break;

                case 3: // 3区の場合
                    if (16 <= cell && cell <= 25)
                    {
                        // 16点~25点の場合
                        return true;
                    }
                    else if (33 <= cell && cell <= 58)
                    {
                        // 33点~58点の場合
                        return true;
                    }
                    else if (65 <= cell && cell <= 90)
                    {
                        // 65点~90点の場合
                        return true;
                    }

                    break;

                case 4: // 4区の場合
                    if (1 <= cell && cell <= 83)
                    {
                        // 1点~83点の場合
                        return true;
                    }

                    break;

                case 5: // 5区の場合
                    if (1 <= cell && cell <= 86)
                    {
                        // 1点~86点の場合
                        return true;
                    }

                    break;

                case 6: // 6区の場合
                    if (1 <= cell && cell <= 24)
                    {
                        // 1点~24点の場合
                        return true;
                    }
                    else if (33 <= cell && cell <= 56)
                    {
                        // 33点~56点の場合
                        return true;
                    }

                    break;

                case 7: // 7区の場合
                    if (1 <= cell && cell <= 33)
                    {
                        // 1点~33点の場合
                        return true;
                    }
                    else if (49 <= cell && cell <= 81)
                    {
                        // 49点~81点の場合
                        return true;
                    }

                    break;

                case 8: // 8区の場合
                    if (1 <= cell && cell <= 32)
                    {
                        // 1点~32点の場合
                        return true;
                    }

                    break;

                default:
                    if (16 <= row && row <= 46) // 16区~46区の場合
                    {
                        if (1 <= cell && cell <= 94)
                        {
                            // 1点~94点の場合
                            return true;
                        }
                    }
                    else if (row == 47) // 47区の場合
                    {
                        if (1 <= cell && cell <= 51)
                        {
                            // 1点~51点の場合
                            return true;
                        }
                    }
                    else if (48 <= row && row <= 83) // 48区~83区の場合
                    {
                        if (1 <= cell && cell <= 94)
                        {
                            // 1点~94点の場合
                            return true;
                        }
                    }
                    else if (row == 84) // 84区の場合
                    {
                        if (1 <= cell && cell <= 6)
                        {
                            // 1点~6点の場合
                            return true;
                        }
                    }

                    break;
            }

            return false;
        }
 
 
Unicodeと日本語(JIS)では漢字の並び順が異なるため、正規表現での範囲指定は利用できません。愚直に一文字ずつ「JIS X 0208」に準拠しているかを判定しています。判定の基準とさせていただいた区・点の情報は、以下のサイトを参考にしております。
 
 
以下のエリアでは「IsUntilJISKanjiLevel2」メソッドを実際に動かした時の挙動を確認できます。
 
●半角カタカナはJIS X 0208 漢字第二水準外
IsUntilJISKanjiLevel2(" 
 ");           
実行結果:
 
●半角カタカナをJIS X 0208 漢字第二水準に含めるかを選択
IsUntilJISKanjiLevel2(" 
 ", 
 );
 
実行結果:
 

.NET 文字列を指定されたバイト数に切り詰める

VBのLeftBやRightBに相当するメソッドを作成しました。
 
以前に投稿した「SubstringByteメソッド」を使用しますので、切り詰めた部分文字列が文字化けする場合は、文字化けした文字を除去します。
 
●左から切り詰める場合
StringUtility utility = new StringUtility();

utility.TruncateByteLeft("1あいうえお", 4);
実行結果:”1あ”
 
●右から切り詰める場合
StringUtility utility = new StringUtility();

utility.TruncateByteRight("1あいうえお", 4);
実行結果:”えお”
 
以下のエリアではTruncateByteLeftメソッドとTruncateByteRightメソッドを実際に動かした時の挙動を確認できます。
 
StringUtility utility = new StringUtility();

utility.TruncateByteLeft(
    
 ", 
 );
実行結果:
“”
 
StringUtility utility = new StringUtility();

utility.TruncateByteRight(
    
 ", 
 );
実行結果:
“”
 
※実行結果の半角スペースは「¸」で表します。
 
TruncateByteLeftメソッド、TruncateByteRightメソッドのソースコードは以下になります。
 
        /// <summary>
        /// 文字列を指定されたバイト長に左側から切り詰めます
        /// </summary>
        /// <param name="target">対象の文字列</param>
        /// <param name="lengthByte">バイト長</param>
        /// <returns>指定されたバイト長に切り詰めた文字列</returns>
        /// <remarks>
        /// <para>文字列が指定されたバイト長に満たない場合は、成型せずに返します</para>
        /// <para>切り詰めた文字列の末尾に全角文字途中の要素が混入している場合は除去します</para>
        /// </remarks>
        public string TruncateByteLeft(string target, int lengthByte)
        {
            // 対象の文字列のバイト長が指定されたバイト長以下の場合
            if (GetByteCount(target) <= lengthByte)
            {
                return target;
            }

            return SubstringByte(target, 0, lengthByte);
        }

        /// <summary>
        /// 文字列を指定されたバイト長に右側から切り詰めます
        /// </summary>
        /// <param name="target">対象の文字列</param>
        /// <param name="lengthByte">バイト長</param>
        /// <returns>指定されたバイト長に切り詰めた文字列</returns>
        /// <remarks>
        /// <para>文字列が指定されたバイト長に満たない場合は、成型せずに返します</para>
        /// <para>切り詰めた文字列の先頭に全角文字途中の要素が混入している場合は除去します</para>
        /// </remarks>
        public string TruncateByteRight(string target, int lengthByte)
        {
            // 対象の文字列のバイト長を取得
            int targetByteCount = GetByteCount(target);

            // 対象の文字列のバイト長が指定されたバイト長以下の場合
            if (targetByteCount <= lengthByte)
            {
                return target;
            }

            return SubstringByte(target, targetByteCount - lengthByte, lengthByte);
        }
 
 
Clipボタンをクリックすると、「StringUtility」というクラスがコピーされます。このソースを「StringUtility.cs」ファイルにコピーするとすぐご利用いただけます。
 

.NET 文字列からバイト単位で部分文字列を取得する

C#、VB.NETで半角を1バイト、全角を2バイトとして、指定したバイト数の部分文字列を取得する、SubstringByteメソッドを作成しました。
 
SubstringByteメソッドに「対象の文字列」、「開始位置のバイト(0から始まるインデックス)」、「部分文字列のバイト数」を指定し実行すると部分文字列を取得できます。
 
StringUtility utility = new StringUtility();

utility.SubstringByte("1あいうえお", 2, 6);
実行結果:”いう”
 
上記のように指定すると「あ」の後ろ半分から「え」の前半分までの6バイトの部分文字列を対象とし、文字化けする最初と最後の文字を除去した「いう」を返します。
 
半角を1バイト、全角を2バイトは文字エンコーディングに「シフトJIS」を指定した場合ですが、「.NET 文字列のバイト数を取得する」と同様に他の文字エンコーディングにも対応しております。
 
以下のエリアではSubstringByteメソッドを実際に動かした時の挙動を確認できます。
 
StringUtility utility = new StringUtility();

utility.SubstringByte(
    
 ", 
 , 
 );
実行結果:
“”
 
※実行結果の半角スペースは「¸」で表します。
 
SubstringByteメソッドのソースコードは以下になります。
 
        /// <summary>
        /// 文字エンコーディング
        /// </summary>
        private Encoding _myEncoding = Encoding.GetEncoding("Shift_JIS");

        /// <summary>
        /// 文字エンコーディング
        /// </summary>
        public Encoding MyEncoding
        {
            get
            {
                return this._myEncoding;
            }
        }

        /// <summary>
        /// 文字列からバイト単位で部分文字列を取得します
        /// </summary>
        /// <param name="target">対象の文字列</param>
        /// <param name="startIndexByte">開始位置インデックスバイト</param>
        /// <param name="lengthByte">部分文字列のバイト数</param>
        /// <returns>文字列からバイト単位で取得した部分文字列</returns>
        public string SubstringByte(string target, int startIndexByte, int lengthByte)
        {
            if (startIndexByte < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "開始位置インデックスバイトが0未満です。");
            }

            if (lengthByte < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "部分文字列のバイト数が0未満です。");
            }

            // 対象の文字列をバイト配列にする
            byte[] targetBytes = MyEncoding.GetBytes(target);

            // 開始位置インデックスバイト+部分文字列のバイト数が文字列のバイト数を超える場合
            if (targetBytes.Length < startIndexByte + lengthByte)
            {
                throw new ArgumentOutOfRangeException("開始位置インデックスバイトまたは部分文字列のバイト数の指定に誤りがあります。");
            }

            // 対象の文字列からバイト単位で部分文字列を取得
            string partialString = MyEncoding.GetString(
                targetBytes, startIndexByte, lengthByte);

            // 先頭に全角文字途中の要素が混入している場合は除去
            partialString = TrimHeadHalfwayDoubleByteCharacter(
                partialString, target, targetBytes, startIndexByte);

            // 末尾に全角文字途中の要素が混入している場合は除去
            partialString = TrimEndHalfwayDoubleByteCharacter(
                partialString, target, targetBytes, startIndexByte, lengthByte);

            return partialString;
        }

        /// <summary>
        /// 先頭に全角文字途中の要素が混入している場合は除去します
        /// </summary>
        /// <param name="partialString">部分文字列</param>
        /// <param name="target">対象の文字列</param>
        /// <param name="targetBytes">対象の文字列のバイト配列</param>
        /// <param name="startIndexByte">開始位置インデックスバイト</param>
        /// <returns>先頭の全角文字途中の要素を除去した部分文字列</returns>
        private string TrimHeadHalfwayDoubleByteCharacter(
            string partialString, string target, byte[] targetBytes, int startIndexByte)
        {
            // 部分文字列が空の場合
            if (partialString == string.Empty)
            {
                return partialString;
            }

            // 開始位置の要素を含む部分文字列を取得
            string leftString = MyEncoding.GetString(targetBytes, 0, startIndexByte + 1);

            // 部分文字列の先頭要素が一致する場合
            if (target[leftString.Length - 1] == partialString[0])
            {
                return partialString;
            }

            // 先頭の全角文字途中の要素を除去
            return partialString.Substring(1);
        }

        /// <summary>
        /// 末尾に全角文字途中の要素が混入している場合は除去します
        /// </summary>
        /// <param name="partialString">部分文字列</param>
        /// <param name="target">対象の文字列</param>
        /// <param name="targetBytes">対象の文字列のバイト配列</param>
        /// <param name="startIndexByte">開始位置インデックスバイト</param>
        /// <param name="lengthByte">部分文字列のバイト数</param>
        /// <returns>末尾の全角文字途中の要素を除去した部分文字列</returns>
        private string TrimEndHalfwayDoubleByteCharacter(
            string partialString, string target, byte[] targetBytes, int startIndexByte,
            int lengthByte)
        {
            // 部分文字列が空の場合
            if (partialString == string.Empty)
            {
                return partialString;
            }

            // 最終要素を含む部分文字列を取得
            string leftString = MyEncoding.GetString(
                targetBytes, 0, startIndexByte + lengthByte);

            // 部分文字列の最終要素が一致する場合
            if (target[leftString.Length - 1] == partialString[partialString.Length - 1])
            {
                return partialString;
            }

            // 末尾の全角文字途中の要素を除去
            return partialString.Substring(0, partialString.Length - 1);
        }
 
 
Clipボタンをクリックすると、「StringUtility」というクラスがコピーされます。このソースを「StringUtility.cs」ファイルにコピーするとすぐご利用いただけます。
g h T
 16,132 Total Views