marunomaruno-memo

marunomaruno-memo

[C][SJIS] Shift_JIS 文字関係のユーティリティ

2012年06月10日 | C / C++
[C][SJIS] Shift_JIS 文字関係のユーティリティ
================================================================================

Shift_JIS 文字関係のユーティリティとして、次のようなものを作った。
ただし、リトル・エンディアン限定。

関数の概要
---
int stoi(const char* s);              日本語の数字列を数値(int)に変換する。
int sjisToDigit(const char* s);       日本語の数字を数値(int)に変換する。
int isSjisDigit(const char* s);       数字("0"~"9")か判断する。
int isSjisHiragana(const char* s);    ひらがなか判断する。
int isSjisKatakana(const char* s);    カタカナか判断する。
---

関数の詳細
---
int stoi(const char* s);

    引数sで与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。
    この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。
    数字列は Shift_JIS の数字以外は記述できない。Shift_JIS の数字以外が出てきた
    時点で変換をやめる。

    引数    s  Shift_JIS で書かれた日本語の数字列

    戻り値  Shift_JIS で書かれた日本語の数字列を変換した数値


int sjisToDigit(const char* s);

    引数sで与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。
    この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ
    けで判断する。

    引数    s  Shift_JIS で書かれた日本語の数字

    戻り値  Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は -1


int isSjisDigit(const char* s);

    引数sで与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。
    この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ
    けで判断する。
     Shift_JIS の"0"~"9"は、0x824f ~ 0x8258.

    引数   s  Shift_JIS で書かれた日本語の数字

    戻り値 数値であれば 1.そうでなければ 0


int isSjisHiragana(const char* s);

    引数sで与えられた Shift_JIS で書かれた日本語がひらがなか判断する。
    この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ
	けで判断する。
    Shift_JIS のひらがなは、0x829F ~ 0x82F1.

    引数   s  Shift_JIS で書かれた日本語文字列

    戻り値 ひらがなであれば 1.そうでなければ 0


int isSjisKatakana(const char* s);

    引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。
    この数字は、C言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ
	けで判断する。
    Shift_JIS のカタカナは、0x829F ~ 0x82F1.

    引数   s  Shift_JIS で書かれた日本語文字列

    戻り値 カタカナであれば 1.そうでなければ 0
---



▲ 公開用ヘッダー

□ sjisUtil.h
---
/*
 ============================================================================
 Name        : sjisUtil.h
 Author      : marunomaruno
 Version     : V1.0, 2012-06-08
 Copyright   : marunomaruno
 Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ(公開用)
 ============================================================================
 */

#ifndef SJIS_UTIL_H_
#define SJIS_UTIL_H_

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語がひらがなか判断する。
 * この数字は、C言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS のひらがなは、0x829F ~ 0x82F1.
 * @param s  Shift_JIS で書かれた日本語文字列
 * @return ひらがなであれば 1.そうでなければ 0
 */
int isSjisHiragana(const char* s);

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS のカタカナは、0x829F ~ 0x82F1.
 * @param s  Shift_JIS で書かれた日本語文字列
 * @return カタカナであれば 1.そうでなければ 0
 */
int isSjisKatakana(const char* s);

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。
 * この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。
 * 数字列は Shift_JIS の数字以外は記述できない。 Shift_JIS の数字以外が出てきた時点で変換をやめる。
 * @param s  Shift_JIS で書かれた日本語の数字列
 * @return  Shift_JIS で書かれた日本語の数字列を変換した数値
 */
int stoi(const char* s);

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * @param s  Shift_JIS で書かれた日本語の数字
 * @return  Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は-1
 */
int sjisToDigit(const char* s);

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS の"0"~"9"は、0x824f ~ 0x8258.
 * @param s  Shift_JIS で書かれた日本語の数字
 * @return 数値であれば 1.そうでなければ 0
 */
int isSjisDigit(const char* s);

#endif /* SJIS_UTIL_H_ */
----


▲ 非公開用ヘッダー

内部的に使うだけの関数の定義。
リトルエンディアンを意識するのはここだけ。

□ sjisUtilInner.h
---
/*
 ============================================================================
 Name        : sjisUtilInner.h
 Author      : marunomaruno
 Version     : V1.0, 2012-06-08
 Copyright   : marunomaruno
 Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ(内部用)
 ============================================================================
 */

#ifndef SJIS_UTIL_INNER_H_
#define SJIS_UTIL_INNER_H_

/**
 * 2 バイト文字 s が 2 バイト文字 start と end の間に入っているか判断する。
 * この 2 バイト文字は、C 言語の文字列の要件を満たす必要はなく、それぞれの引数からの 2 バイトだけで判断する。
 * @param s  Shift_JIS で書かれた 2 バイト文字
 * @param start  Shift_JIS で書かれた 2 バイト文字で、検査する範囲の始端
 * @param end  Shift_JIS で書かれた 2 バイト文字で、検査する範囲の終端
 * @return 範囲内であれば 1.そうでなければ 0
 */
static int inRange2byteChar(const char* s, const char* start, const char* end);

#endif /* SJIS_UTIL_INNER_H_ */
---


▲ モジュール

□ sjisUtil.c
---
/*
 ============================================================================
 Name        : sjisUtil.c
 Author      : marunomaruno
 Version     : V1.0, 2012-06-08
 Copyright   : marunomaruno
 Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ
 ============================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sjisUtil.h"
#include "sjisUtilInner.h"

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。
 * この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。
 * 数字列は Shift_JIS の数字以外は記述できない。 Shift_JIS の数字以外が出てきた時点で変換をやめる。
 * @param s  Shift_JIS で書かれた日本語の数字列
 * @return  Shift_JIS で書かれた日本語の数字列を変換した数値
 */
int stoi(const char* s) {
    int num = 0;
    int i;

    for (i = 0; i < strlen(s); i += 2) {
        int digit = 0;
        if ((digit = sjisToDigit(&s[i])) == -1) {
            break;
        }
        num = num * 10 + digit;
    }

    return num;
}

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * @param s  Shift_JIS で書かれた日本語の数字
 * @return  Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は -1
 */
int sjisToDigit(const char* s) {
    if (!isSjisDigit(s)) {
        return -1;
    }

    return s[1] - 0x4f;
}

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS の"0"~"9"は、0x824f ~ 0x8258.
 * @param s  Shift_JIS で書かれた日本語の数字
 * @return 数値であれば 1.そうでなければ 0
 */
int isSjisDigit(const char* s) {
    return inRange2byteChar(s, "0", "9");
}

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語がひらがなか判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS のひらがなは、0x829F ~ 0x82F1. 長音(ー)もひらがなとする。
 * @param s  Shift_JIS で書かれた日本語文字列
 * @return ひらがなであれば 1.そうでなければ 0
 */
int isSjisHiragana(const char* s) {
    return inRange2byteChar(s, "ぁ", "ん") || inRange2byteChar(s, "ー", "ー");
}

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS のカタカナは、0x829F ~ 0x82F1. 長音(ー)もカタカナとする。
 * @param s  Shift_JIS で書かれた日本語文字列
 * @return カタカナであれば 1.そうでなければ 0
 */
int isSjisKatakana(const char* s) {
    return inRange2byteChar(s, "ァ", "ヶ") || inRange2byteChar(s, "ー", "ー");
}

/**
 * 2 バイト文字sが 2 バイト文字startとendの間に入っているか判断する。
 * この 2 バイト文字は、C 言語の文字列の要件を満たす必要はなく、それぞれの引数からの 2 バイトだけで判断する。
 * @param s  Shift_JIS で書かれた 2 バイト文字
 * @param start  Shift_JIS で書かれた 2 バイト文字で、検査する範囲の始端
 * @param end  Shift_JIS で書かれた 2 バイト文字で、検査する範囲の終端
 * @return 範囲内であれば 1.そうでなければ 0
 */
int inRange2byteChar(const char* s, const char* start, const char* end) {
    typedef union {
        unsigned char str[2];
        unsigned short num;
    } DoubleByteChar;

    DoubleByteChar value;
    DoubleByteChar startValue;
    DoubleByteChar endValue;

    value.str[0] = s[1];
    value.str[1] = s[0];

    startValue.str[0] = start[1];
    startValue.str[1] = start[0];

    endValue.str[0] = end[1];
    endValue.str[1] = end[0];

    return (startValue.num <= value.num) && (value.num <= endValue.num);
}
---


▲ テスト

上記の関数を確認するためのテスト・プログラム。
数字、ひらがな、かたかなの限界値をチェックする。
また、リトルエンディアン前提なので、バイト順が逆転している場合に範囲になるかどう
かもチェックする。


□ test.c
---
/*
 ============================================================================
 Name        : test.c
 Author      : marunomaruno
 Version     : V1.0, 2012-06-08
 Copyright   : marunomaruno
 Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティのテスト・
プログラム
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "sjisUtil.h"

int main(void) {
    printf("test start\n");

    char hiragana[][3] = { "あ", "い", "か", "さ", "た", "や", "ら", "わ", "っ",
 "ん",
            "ー", };
    char katakana[][3] = { "ア", "イ", "ウ", "サ", "タ", "ヤ", "ラ", "ワ", "ッ",
 "ン",
            "ー", };
    char num[][3] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
 };
    char other[][3] = { { 0x4e, 0x82, '0' }, { 0x59, 0x82, '0' }, { 0x9e, 0x82,
            '0' }, { 0xf2, 0x82, '0' }, { 0xfc, 0x82, '0' },
            { 0x97, 0x823, '0' }, "氓", "A", "一", "所", };
    int i;

    // 長音も含め、ひらがな、カタカナ、数字
    for (i = 0; i < 11; i++) {
        assert(isSjisHiragana(hiragana[i]));
    }

    for (i = 0; i < 11; i++) {
        assert(isSjisKatakana(katakana[i]));
    }

    for (i = 0; i < 10; i++) {
        assert(isSjisDigit(num[i]));
    }

    // 長音
    assert(isSjisKatakana("ー"));
    assert(isSjisHiragana("ー"));

    // ひらがな、カタカナ、数字でない
    for (i = 0; i < 10; i++) {
        assert(!isSjisHiragana(katakana[i]));
        assert(!isSjisHiragana(num[i]));
        assert(!isSjisHiragana(other[i]));
    }

    for (i = 0; i < 10; i++) {
        assert(!isSjisKatakana(hiragana[i]));
        assert(!isSjisKatakana(num[i]));
        assert(!isSjisKatakana(other[i]));
    }

    for (i = 0; i < 10; i++) {
        assert(!isSjisDigit(hiragana[i]));
        assert(!isSjisDigit(katakana[i]));
        assert(!isSjisDigit(other[i]));
    }

    // 全角数字を数値に変換
    for (i = 0; i < 10; i++) {
        assert(i == sjisToDigit(num[i]));
    }

    // 全角数字を数値に変換
    assert(1234 == stoi("1234"));
    assert(1234567890 == stoi("1234567890"));
    assert(0 == stoi("01234567890"));
    assert(123456 == stoi("12345607890"));

    // 文字列としての検査
    char* s1;
    s1 = "あいうえおー";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisHiragana(&s1[i])) {
            assert(0);
        }
    }
    assert(1);

    s1 = "あいうえおー1";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisHiragana(&s1[i])) {
            assert(1);
        }
    }
    assert(i >= strlen(s1));

    s1 = "あいうえおーア";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisHiragana(&s1[i])) {
            assert(1);
        }
    }
    assert(i >= strlen(s1));

    s1 = "アイウエオー";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisKatakana(&s1[i])) {
            assert(0);
        }
    }
    assert(1);

    s1 = "アイウエオー1";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisKatakana(&s1[i])) {
            assert(1);
        }
    }
    assert(i >= strlen(s1));

    s1 = "アイウエオーあいうえおア";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisKatakana(&s1[i])) {
            assert(1);
        }
    }
    assert(i >= strlen(s1));

    printf("test end\n");
    return EXIT_SUCCESS;
}
---

                                                                            以上


最新の画像もっと見る

コメントを投稿