Delphi Programming / Object Pascal
[掲載 2023年05月31日] [更新 2023年06月07日] Delphi 一般・その他
区点コードを Unicode のコードポイントに変換
動作確認等
Windows 10 ビルド 19043 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32
区点コードを Unicode のコードポイントに変換
当サイトに,JIS の区点コード表を掲載していますが,JIS の区点コードは,Windows に実装されている文字符号化方式ではありません.区点コードを Unicode のコードポイントに変換して,それを参照して文字を表示しています.その際に使用した,JIS の区点コードを Unicode のコードポイントに変換する手順です.
JIS で制定されている文字にはサロゲートペアの文字があります.HTML で,Unicode のコードポイントを参照して文字を表示する場合,認識してくれないサロゲートペアがあります.そこで,本ページのサンプルではその対策処理をしています.
01_ JIS X 0208 の区点コードを Unicode のコードポイントに変換
JIS X 0208 の区点コードを Unicode のコードポイントに変換する例です.
区と点の値に $20 を加算した値に,エスケープシーケンスと呼ばれるバイトデータを追加して,コードページ 50222 のバイトデータを生成します.このバイトデータをエンコードして Unicode の文字とします.その文字のコードポイントを取得する関数を作成して使用しています.JIS X 0208 のエスケープシーケンスは $1B $24 $42 です.1 文字だけが対象なので,文字列終端のエスケープシーケンス $1B $28 $42 は省略しています.
下図が実行結果です.文字がない場合はコードポイントの値が $30FB となります.
図1
実行結果
値は左から
区点コード
その区点コードの文字
Unicode のコードポイント
文字がない場合は $30FB
copy code
implementation
uses
MECSUtils;
{$R *.dfm}
var
FEsc0208 : array [0 ..4 ] of Byte = ($1B , $24 , $42 , $00 , $00 );
// -----------------------------------------------------------------------------
// JIS X 0208 の区点コードを Unicode のコードポイントに変換
// 変換できなかった時 ( 文字がない時 ) は $30FB 「・」を返す
// -----------------------------------------------------------------------------
function KuTen2CodePoint(AKuNo: Byte; ATenNo: Byte): Integer;
// 全角チルダ / 波ダッシュ問題の対応処理
function Fix_JISX0208ToUnicode(const AUniCode: Integer): Integer;
begin
Result := AuniCode;
case AUniCode of
$2014 : Result := $2015 ;
$203E : Result := $FFE3 ;
$2016 : Result := $2225 ;
$2212 : Result := $FF0D ;
$301C : Result := $FF5E ;
$00A2 : Result := $FFE0 ;
$00A3 : REsult:= $FFE1 ;
$00AC : Result:= $FFE2 ;
end ;
end ;
var
LStream : TMemoryStream;
LStrList : TStringList;
LUniStr : string ;
Lucs4Text : UCS4String;
begin
Result := $30FB ;
if (AKuNo <= 8 ) or (AKuNo >= 16 ) and ((AKuNo <= 84 )) then begin
FEsc0208[3 ] := AKuNo + $20 ;
FEsc0208[4 ] := ATenNo + $20 ;;
LStream := TMemoryStream.Create;
LStrList := TStringList.Create;
try
LStream.Write (FEsc0208[0 ], Length(FEsc0208));
LStream.Position := 0 ;
LStrList.LoadFromStream(LStream, TEncoding.GetEncoding(50222 ));
// サロゲートペア対策.UTF-32 にして処理
LUniStr := LStrList[0 ];
Lucs4Text := MECSUtils.UTF16ToUTF32(LUniStr);
Result := Fix_JISX0208ToUnicode(Lucs4Text[0 ]);
finally
FreeAndNil(LStream);
FreeAndNil(LStrList);
end ;
end ;
end ;
// =============================================================================
// 上の関数の使用例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LKuNo : Integer;
LTenNo : Integer;
LUniStr : string ;
LCode : Integer;
LText : string ;
begin
Memo1.Lines.Clear;
LKuNo := 16 ;
LTenNo := 11 ;
LCode := KuTen2CodePoint(LKuNo, LTenNo);
LUniStr := CodePointToUTF16(LCode);
LText := IntToStr(LKuNo) + '-' + IntToStr(LTenNo);
LText := LText + ' ' + LUniStr + ' ' + 'U+' + IntToHex(LCode, 4 );
Memo1.Lines.Add(LText);
LKuNo := 16 ;
LTenNo := 96 ;
LCode := KuTen2CodePoint(LKuNo, LTenNo);
LUniStr := CodePointToUTF16(LCode);
LText := IntToStr(LKuNo) + '-' + IntToStr(LTenNo);
LText := LText + ' ' + LUniStr + ' ' + 'U+' + IntToHex(LCode, 4 );
Memo1.Lines.Add(LText);
end ;
上のサンプルでは,TStringList にバイトデータを読み込んでいます.以下は,コードページ 50222 の文字列型の文字列のデータとしてバイトデータを読み込んで処理する例です.
copy code
type
F50222String = type AnsiString(50222 );
// -----------------------------------------------------------------------------
// JIS X 0208 の区点コードを Unicode のコードポイントに変換
// 変換できなかった時 ( 文字がない時 ) は $30FB 「・」を返す
// -----------------------------------------------------------------------------
function KuTen2CodePoint(AKuNo: Byte; ATenNo: Byte): Integer;
// 全角チルダ / 波ダッシュ問題の対応処理
function Fix_JISX0208ToUnicode(const AUniCode: Integer): Integer;
begin
Result := AuniCode;
case AUniCode of
$2014 : Result := $2015 ;
$203E : Result := $FFE3 ;
$2016 : Result := $2225 ;
$2212 : Result := $FF0D ;
$301C : Result := $FF5E ;
$00A2 : Result := $FFE0 ;
$00A3 : REsult:= $FFE1 ;
$00AC : Result:= $FFE2 ;
end ;
end ;
var
LStream : TMemoryStream;
L50222Str : F50222String;
LUniStr : string ;
Lucs4Text : UCS4String;
begin
Result := $30FB ;
if (AKuNo <= 8 ) or (AKuNo >= 16 ) and ((AKuNo <= 84 )) then begin
FEsc0208[3 ] := AKuNo + $20 ;
FEsc0208[4 ] := ATenNo + $20 ;;
LStream := TMemoryStream.Create;
try
LStream.Write (FEsc0208[0 ], Length(FEsc0208));
SetLength(L50222Str, LStream.Size);
LStream.Position := 0 ;
LStream.Read (L50222Str[1 ], LStream.Size);
// サロゲートペア対策.UTF-32 にして処理
LUniStr := L50222Str;
Lucs4Text := MECSUtils.UTF16ToUTF32(LUniStr);
Result := Fix_JISX0208ToUnicode(Lucs4Text[0 ]);
finally
FreeAndNil(LStream);
end ;
end ;
end ;
このサンプルでは,MECSUtils に MecsMappingFix_JISX0208ToUnicode という関数のコードを利用して「全角チルダ / 波ダッシュ問題」の修正をしています.
修正前
修正後
—
U+2014
―
U+2015
‾
U+203E
 ̄
U+FFE3
‖
U+2016
∥
U+2225
−
U+2212
-
U+FF0D
〜
U+301C
~
U+FF5E
¢
U+00A2
¢
U+FFE0
£
U+00A3
£
U+FFE1
¬
U+00AC
¬
U+FFE2
02_ JIS X 0213 の区点コードを Unicode のコードポイントに変換
JIS X 0213 の区点コードの符号化は,ISO-2022-JP-2004 で制定されていますが,Windows には実装されていません. Python, PHP には実装されているようです.
以下のリンクに掲載している区点コード表は,ウィキペティアに掲載されている表をエクセルに取り込んで利用して作成しています.
次の最初のリンクの記事の方法は,ウェブページの表を指定して取り込みます.2 番目のリンクのサンプルプログラムは,ウェブページの表を全てエクセルのシートに取り込みます.
ウェブページの表をエクセルに読み込んだら,名前を付けて保存します.カンマ区切りテキスト (CSV) として保存することも考えられます.CSV ファイルとして保存した場合,TStringList 等に読み込む時は UTF-8 でエンコードします.U+ がない行を削除すれば,区点コード情報のテキストになります.
JIS X 0213 には,サロゲートペアの文字,結合文字があります.ウィキペティアに掲載されている非漢字一覧の Unicode のコードポイントは,「全角チルダ / 波ダッシュ問題」対応した値にはなっていません.
03_ JIS X 0212 の区点コードを Unicode のコードポイントに変換
JIS X 0212 の区点コードを Unicode のコードポイントに変換する例です.
区と点に $20 を加算した値に,エスケープシーケンスと呼ばれるバイトデータを追加して,コードページ 50222 のバイトデータを生成します.このバイトデータをエンコードして Unicode の文字とします.その文字のコードポイントを取得する関数を作成して使用しています.JIS X 0212 のエスケープシーケンスは $1B $24, $28 $44 です.1 文字だけが対象なので,文字列終端のエスケープシーケンス $1B $28 $42 は省略しています.
下図が実行結果です.文字がない場合はコードポイントの値が $30FB となります.
図2
実行結果
値は左から
区点コード
その区点コードの文字
Unicode のコードポイント
文字がない場合は $30FB
copy code
implementation
uses
MECSUtils;
{$R *.dfm}
var
FEsc0212 : array [0 ..5 ] of Byte = ($1B , $24 , $28 , $44 , $00 , $00 );
// -----------------------------------------------------------------------------
// JIS X 0212 の区点コードを Unicode のコードポイントに変換
// 変換できなかった時 ( 文字がない時 ) は $30FB 「・」を返す
// -----------------------------------------------------------------------------
function KuTen2CodePoint(AKuNo: Byte; ATenNo: Byte): Integer;
var
LStream : TMemoryStream;
LStrList : TStringList;
LUniStr : string ;
Lucs4Text : UCS4String;
begin
FEsc0212[4 ] := AKuNo + $20 ;
FEsc0212[5 ] := ATenNo + $20 ;;
LStream := TMemoryStream.Create;
LStrList := TStringList.Create;
try
LStream.Write (FEsc0212[0 ], Length(FEsc0212));
LStream.Position := 0 ;
LStrList.LoadFromStream(LStream, TEncoding.GetEncoding(50222 ));
// サロゲートペア対策.UTF-32 にして処理
LUniStr := LStrList[0 ];
Lucs4Text := MECSUtils.UTF16ToUTF32(LUniStr);
Result := Lucs4Text[0 ];
finally
FreeAndNil(LStream);
FreeAndNil(LStrList);
end ;
end ;
// =============================================================================
// 上の関数の使用例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LKuNo : Integer;
LTenNo : Integer;
LUniStr : string ;
LCode : Integer;
LText : string ;
begin
Memo1.Lines.Clear;
LKuNo := 2 ;
LTenNo := 78 ;
LCode := KuTen2CodePoint(LKuNo, LTenNo);
LUniStr := CodePointToUTF16(LCode);
LText := IntToStr(LKuNo) + '-' + IntToStr(LTenNo);
LText := LText + ' ' + LUniStr + ' ' + 'U+' + IntToHex(LCode, 4 );
Memo1.Lines.Add(LText);
LKuNo := 2 ;
LTenNo := 88 ;
LCode := KuTen2CodePoint(LKuNo, LTenNo);
LUniStr := CodePointToUTF16(LCode);
LText := IntToStr(LKuNo) + '-' + IntToStr(LTenNo);
LText := LText + ' ' + LUniStr + ' ' + 'U+' + IntToHex(LCode, 4 );
Memo1.Lines.Add(LText);
end ;
04_ GB コードを Unicode のコードポイントに変換
GB コードを Unicode のコードポイントに変換する例です.
GB コードの値に,エスケープシーケンスと呼ばれるバイトデータを追加して,コードページ 50222 のバイトデータを生成します.このバイトデータをエンコードして Unicode の文字とします.その文字のコードポイントを取得する関数を作成して使用しています.GB コードのエスケープシーケンスは $1B $24 $29 $41 です.1 文字だけが対象なので,文字列終端のエスケープシーケンス $1B $28 $42 は省略しています.
下図が実行結果です.文字がない場合はコードポイントの値が $003F となります.
図3
実行結果
値は左から
GB コード
その区点コードの文字
Unicode のコードポイント
文字がない場合は $003F
copy code
implementation
uses
MECSUtils;
{$R *.dfm}
var
FEscGB : array [0 ..5 ] of Byte = ($1B , $24 , $29 , $41 , $00 , $00 );
// -----------------------------------------------------------------------------
// GB コードを Unicode のコードポイントに変換
// 変換できなかった時 ( 文字がない時 ) は $003F 「?」を返す
// -----------------------------------------------------------------------------
function GB2CodePoint(AGBCode: Integer): Integer;
var
LStream : TMemoryStream;
LStrList : TStringList;
LUniStr : string ;
Lucs4Text : UCS4String;
begin
FEscGB[4 ] := Hi(AGBCode);
FEscGB[5 ] := Lo(AGBCode);;
LStream := TMemoryStream.Create;
LStrList := TStringList.Create;
try
LStream.Write (FEscGB[0 ], Length(FEscGB));
LStream.Position := 0 ;
LStrList.LoadFromStream(LStream, TEncoding.GetEncoding(50222 ));
// サロゲートペア対策.UTF-32 にして処理
LUniStr := LStrList[0 ];
Lucs4Text := MECSUtils.UTF16ToUTF32(LUniStr);
Result := Lucs4Text[0 ];
finally
FreeAndNil(LStream);
FreeAndNil(LStrList);
end ;
end ;
// =============================================================================
// 上の関数の使用例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LGBCode : Integer;
LUniStr : string ;
LCode : Integer;
LText : string ;
begin
Memo1.Lines.Clear;
LGBCode := $3032 ;
LCode := GB2CodePoint(LGBCode);
LUniStr := CodePointToUTF16(LCode);
LText := '$' + IntToHex(LGBCode, 4 )
+ ' ' + LUniStr + ' ' + 'U+' + IntToHex(LCode, 4 );
Memo1.Lines.Add(LText);
LGBCode := $2661 ;
LCode := GB2CodePoint(LGBCode);
LUniStr := CodePointToUTF16(LCode);
LText := '$' + IntToHex(LGBCode, 4 )
+ ' ' + LUniStr + ' ' + 'U+' + IntToHex(LCode, 4 );
Memo1.Lines.Add(LText);
end ;
ASCII 文字と JIS X 0208 で制定されている文字は,日本語を扱う処理系であれば全てが処理可能です.これらは環境に依存しない文字とされています.コードページ 50222 のバイナリデータを調べると,その文字が JIS X 0208 で制定されているかを調べることができます.
このサンプルは,コードページ 50222 の文字のエスケープシーケンスを調べて,文字が JIS X 0208 で制定されているかを判定します.JIS X 0208 で制定されていれば,区点コードを取得して表示します.ASCII 文字でもなく,JIS X 0208 にも制定されていない文字の場合は [環境依存] とします.
このサンプルは,MECSUtils の MecsMappingFix_UnicodeToJISX0208 関数を使用して「全角チルダ / 波ダッシュ問題」と「JIS2004 問題 (新常用漢字問題)」を修正しています.
図4
文字の区点コードと [環境依存]
copy code
implementation
uses
MECSUtils;
{$R *.dfm}
type
F50222String = type AnsiString(50222 );
// -----------------------------------------------------------------------------
// 引数の文字の JIS X 0208 ( JIS 基本漢字 ) の区点コードを取得
// 戻り値の最下位バイトが点,その上のバイトが区の値
// なければ 0 を返す
// -----------------------------------------------------------------------------
function Str2KuTenCode(AStr: string ): Integer;
var
LUniStr : string ;
L50222Str : F50222String;
LBytes : TBytes;
LkuNo : Byte;
begin
Result := 0 ;
LUniStr := MecsMappingFix_UnicodeToJISX0208(AStr);
L50222Str := LUniStr;
LBytes := MecsBytesOf(L50222Str);
if Length(LBytes) >= 8 then begin
LKuNo := LBytes[3 ] - $20 ;
if (LKuNo <= 8 ) or (LKuNo >= 16 ) and ((LKuNo <= 84 )) then begin
if (LBytes[0 ] = $1B ) and (LBytes[1 ] = $24 ) and (LBytes[2 ] = $42 ) then begin
Result := (LkuNo shl 8 ) + (LBytes[4 ] - $20 );
end ;
end ;
end ;
end ;
// =============================================================================
// 上の関数の使用例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LKuNo : Integer;
LTenNo : Integer;
LUniStr : string ;
LCode : Integer;
LText : string ;
begin
Memo1.Lines.Clear;
LUniStr := '東' ;
if Ord(LUniStr[1 ]) <= $7F then begin
LText := LUniStr + ' ' + 'ASCII' ;
end else begin
LCode := Str2KuTenCode(LUniStr);
if LCode = 0 then begin
LText := LUniStr + ' ' + '[環境依存]' ;
end else begin
LKuNo := Hi(LCode);
LTenNo := Lo(LCode);
LText := LUniStr + ' ' + IntToStr(LKuNo) + '-' + IntToStr(LTenNo);
end ;
end ;
Memo1.Lines.Add(LText);
LUniStr := '髙' ;
if Ord(LUniStr[1 ]) <= $7F then begin
LText := LUniStr + ' ' + 'ASCII' ;
end else begin
LCode := Str2KuTenCode(LUniStr);
if LCode = 0 then begin
LText := LUniStr + ' ' + '[環境依存]' ;
end else begin
LKuNo := Hi(LCode);
LTenNo := Lo(LCode);
LText := LUniStr + ' ' + IntToStr(LKuNo) + '-' + IntToStr(LTenNo);
end ;
end ;
Memo1.Lines.Add(LText);
end ;
06_ CP932 のコードポイントを Unicode のコードポイントに変換
一凛のコード表には Shift_JIS のコード表も掲載しています.
Shift_JIS の各文字のコードは,コードページ 932 のコードポイントです,そのコードポイントを Unicode のコードポイントに変換する例です.Shift_JIS の文字には,サロゲートペアの文字も結合文字もありません.全て,Unicode では 1 エレメント (2 バイト) の文字です.
JIS X 0208 の文字に,半角カタカナと以下のページに掲載している文字を追加したのが Shift_JIS の文字です.しかがって,上のサンプルの方法を使用すれば,文字がない猟奇を検出できます.ASCII 文字はどの符号化方式でも使用可能です.
図5
実行結果
$889F を Unicdoe のコードポイントに変換
copy code
implementation
{$R *.dfm}
// =============================================================================
// CP932 のコードポイント ( 文字コード ) を Unicode のコードポイントに変換
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LSjisCode : Integer;
LSjisStr : AnsiString;
LUniStr : string ;
LUniCode : Integer;
LText : string ;
begin
Memo1.Lines.Clear;
LSjisCode := $889F ;
if LSjisCode <= $FF then begin
LSjisStr := AnsiChar(LSjisCode);
end else begin
LSjisStr := AnsiChar(Hi(LSjisCode)) + AnsiChar(Lo(LSjisCode));
end ;
LUniStr := string (LSjisStr);
LUniCode := Ord(LUniStr[1 ]);
LText := LSjisStr + ' ' + LUniStr
+ ' $' + IntToHex(LSjisCode, 2 ) + ' U+' + IntToHex(LUniCode, 4 );
Memo1.Lines.Add(LText);
end ;