Sybase ASE文字列のUTF-8バイト数、文字数を求める関数

Sybase ASE DBMSでUTF-8バイト数、文字数を求める関数について見ていきます。 Sybase ASE Non-UnicodeエンコーディングであるEUC-KSCからUnicodeエンコーディングであるUTF-8に変換するプロジェクトで作って使った関数だ。

* UTF-8エンコーディング注: UTF-8 – ウィキペディア、私たち全員の百科事典(wikipedia.org)

EUC-KSCでvarchar(10)として宣言された列をUTF-8に変換するときは、最大バイト数と文字数を確認する必要があります。

UTF-8に変換するときの最大バイト数と文字数は、文字列列の最大長をどのくらい宣言する必要があるかを判断するために必要です。宣言された長さではなく、EUC-KSCエンコーディングで保存された値に対してUTF-8エンコーディングに変換されたバイト数と文字数を計算して最大値を求めるためにこの関数を使用しました。

DBMSはSybase ASE v15.5でテストしました。他のバージョンではテストしていませんが、うまく動作すると思います。

Sybase ASEはMS-SQLと非常によく似ており、MS-SQLでも特別な変更なしにそのまま使用できます。

1. UTF-8バイト数を求める関数

IF OBJECT_ID('dbo.GET_UTF8_BYTE') IS NOT NULL
  DROP FUNCTION dbo.GET_UTF8_BYTE
GO

CREATE FUNCTION dbo.GET_UTF8_BYTE
(
    @IN_VAL VARCHAR(4000)
)
RETURNS INT
AS
BEGIN
  DECLARE @CHK_LENGTH INT, @I INT, @CHK_BYTE INT, @BYTE_LEN INT, @UTF8_BYTE_LEN INT, @SUM_UTF8_BYTE_LEN INT

  SELECT @I = 1, @CHK_LENGTH = LEN(@IN_VAL), @SUM_UTF8_BYTE_LEN = 0

  WHILE @I <= @CHK_LENGTH
  BEGIN
    SELECT @CHK_BYTE = ASCII(SUBSTRING(@IN_VAL, @I, 1))

    IF @CHK_BYTE >= 128 -- ASCII 코드가 alpha, numeric, control 문자 범위를 넘는 경우(2 Byte 문자일 경우)
        SELECT @BYTE_LEN = 2, @UTF8_BYTE_LEN = 3
    ELSE
        SELECT @BYTE_LEN = 1, @UTF8_BYTE_LEN = 1

    SELECT @SUM_UTF8_BYTE_LEN = @SUM_UTF8_BYTE_LEN + @UTF8_BYTE_LEN, @I = @I + @BYTE_LEN
  END

  RETURN @SUM_UTF8_BYTE_LEN
END
GO

このコードは、次の事実に基づいて書かれています。

  • Non-Unicode 1 ByteのASCIIコードが128以上の場合、2バイト文字(漢、中、日など)
  • Non-Unicode ハングル 1 文字 (2 バイト) は UTF-8 エンコードで 3 バイトとして保存される

主なコードの説明は次のとおりです。

  • 行18:入力文字列を1バイトずつ読みます。
  • 行21:読み取ったバイトのASCIIコードが128以上の場合、スキップするバイト長を2に設定し、UTF-8バイト数を3増加
  • 行23:読み取られたバイトのASCIIコードが128未満の場合、スキップされるバイト長を1に設定し、UTF-8バイト数を1増加
  • 行25:UTF-8バイト数の合計を累積、WHILE反復ステートメントの条件変数@ I値をスキップするバイト長だけ増加

2. UTF-8文字数の取得関数

上記の関数を次のように変換すると、UTF-8バイト数ではなく文字数が得られます。

IF OBJECT_ID('dbo.GET_CHAR_CNT') IS NOT NULL
  DROP FUNCTION dbo.GET_CHAR_CNT
GO

CREATE FUNCTION dbo.GET_CHAR_CNT
(
    @IN_VAL VARCHAR(4000)
)
RETURNS INT
AS
BEGIN
  DECLARE @CHK_LENGTH INT, @I INT, @CHK_BYTE INT, @BYTE_LEN INT, @UTF8_BYTE_LEN INT, @SUM_UTF8_BYTE_LEN INT, @SUM_CHAR_CNT INT

  SELECT @I = 1, @CHK_LENGTH = LEN(@IN_VAL), @SUM_UTF8_BYTE_LEN = 0, @SUM_CHAR_CNT = 0

  WHILE @I <= @CHK_LENGTH
  BEGIN
    SELECT @CHK_BYTE = ASCII(SUBSTRING(@IN_VAL, @I, 1))

    IF @CHK_BYTE >= 128 -- ASCII 코드가 alpha, numeric, control 문자 범위를 넘는 경우(2 Byte 문자일 경우)
        SELECT @BYTE_LEN = 2, @UTF8_BYTE_LEN = 3
    ELSE
        SELECT @BYTE_LEN = 1, @UTF8_BYTE_LEN = 1

    SELECT @SUM_UTF8_BYTE_LEN = @SUM_UTF8_BYTE_LEN + @UTF8_BYTE_LEN, @I = @I + @BYTE_LEN
    SELECT @SUM_CHAR_CNT = @SUM_CHAR_CNT + 1
  END

  --RETURN @SUM_UTF8_BYTE_LEN
  RETURN @SUM_CHAR_CNT
END

16行目から始まるWHILEイテレーションステートメントは、@ I変数を条件で終了します。 @BYTE_LEN変数は、読み込まれたバイトのASCIIコードに従って2(2バイト文字)または1(1バイト文字)に設定され、25行目で@ Iに加算されます。このロジックでは、WHILE反復文は文字数だけ繰り返し実行されます。

26行目で@SUM_CHAR_CNTを1ずつ増やすのは、繰り返し回数が文字数だからです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ja日本語