Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2022年11月26日] [更新 2022年12月21日] Delphi 一般・その他
   
半角カタカナの小文字を大文字に変換
動作確認等 Windows 10 ビルド 19043 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32
[整理番号 - 1210]







文字の置き換えの参考としてのサンプル


記事の前半は,半角カタカナの小文字を半角カタカナの大文字に変換するサンプルですが,テキスト内の文字の置き換えの参考となるサンプルも掲載しています.例えば,全角の文字を半角の文字にしたり,ひらがなをカタカナに変換したりすることも文字の置き換えです.
文字の置き換えは,文字のコードポイントの値,つまり,バイト値を書き換えることです.Unicdode (UTF-16) や Shift_JIS のテキストであれば,文字を直接指定して置き換えることもできますが,本ページては,主に,コードポイントの値を使用して処理します.
その他,逆の変換や関連すると思われるサンプルも掲載しています.

以下のページにも文字の置き換えのサンプルがありますが,コードページが Unicode (UTF-16) の文字が対象となっています.



ASCII コード内の半角の英字 (半角アルファベット) の場合は,UpperCase 関数と LowerCase 関数を使用して,大文字,小文字に変換できます.



本ページで使用しているコードページとは,Unicode (UTF-16) や Shift_JIS などの文字符号化方式に対する呼称です.コードポイントは,文字に割り当てられている値,つまり「文字のコード」のことです.




Microsoft IME では,再変換の機能を利用すると,入力が確定した全角カタカナと半角カタカナなどの相互変換ができます.
カタカナの入力は,例えば「あ」を変換して,未確定の状態で [F7] キーほ押下すれば全角の「ア] になります.[F8] キーを押下すれば半角の「ア」になります.

下図は,入力が確定した全角の「フ」を変換す例です.
対象の文字を選択して [変換] キーを押します.これが再変換です.



図1
対象の文字を選択して [変換] キー押下
  • [全] の ア になっている

変換候補から [全][小] の ァ を選択すると,全角カタカナの小文字になります.


図2
[全][小] の ァ を選択
  • 全角カタカナの小文字になる

上の図の状態で [F8] キーを押すと,半角カタカナの小文字になります.
半角カタカナの小文字を全角カタカナの小文字にするには,文字を選択して [変換] キー押し,[F7] キーを押します.全角カタカナの小文字を全角カタカナの大文字にするには,文字を選択して [変換] キーを押して,変換候補のリストから選択します.


図3
全角カタカナの小文字を半角に変換
  • 上の図の状態で [F8] キーを押下
  • 半角カタカナの小文字になる

下図のように,複数の文字を同時に再変換することもできます.未確定の状態で [F7] を押下すると全角文字に,[F8] を押下すると半角文字になります.
変換候補のリストにあるように,カタカナとひらがなの相互変換も可能です.



図4
複数の文字の同時変換
  • 複数の文字を選択
  • [変換] キーを押下
  • カタカナとひらがなの再変換も可能
  • [環境依存] は,半角カタカナ


[備考]
ひらがな,カタカの小文字は 9 文字あります.ひらがなの半角文字はありません.
「 」などの記号類にも,全角文字と半角文字があります.





01_半角カタカナ


半角カタカナには「ガギグゲゴ」などの濁音文字と,「パピプペポ」などの半濁音文字はありません.半角カナの濁音文字は,例えば「カ」 と「゙」,「ハ」と「゚」の 2 文字を使用します.これらが結合して結合文字となり,1 つの文字として表示されます.


カキクケコ ハヒフヘホ 

ガギグゲゴ パピプペポ 

ギャギュギョ パャピュピョ



下の表は,コードページが 932, つまり Shift_JIS の 1 バイト文字の文字コードです.
7 ビットの文字である ASCII コードは $00 から $7F までです.
$80 から $FF までは Shift_JIS で使用していてる文字の領域で,この領域に半角カナがあります.半角カナの小文字は $A7 から $AF にあります.
$80 ~ $9F, $E0 ~ $FF には文字の割り当てはありません.これらは,2 バイト文字の先頭バイト値として使用されています.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
00
10
20 ! " # $ % & ' ( ) * + , - . /
30 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
40 @ A B C D E F G H I J K L M N O
50 P Q R S T U V W X Y Z [ \ ] ^ _
60 ` a b c d e f g h i j k l m n o
70 p q r s t u v w x y z { | } ~
80
90
A0
B0 ソ
C0
D0
E0
F0


Unicode (UTF-16) の,Shift_JIS の 1 バイト文字に相当するのは $0000 ~ $00FF です.$0000 ~ $007F までは,Shift_JIS の $00 ~ $7F と同じ文字が割り当てられています.
$0080 ~ $00FF には,Shift_JIS とは全く違う文字が割り当てられています.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
U+0020   ! " # $ % & ' ( ) * + , - . /
U+0030 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
U+0040 @ A B C D E F G H I J K L M N O
U+0050 P Q R S T U V W X Y Z [ \ ] ^ _
U+0060 ` a b c d e f g h i j k l m n o
U+0070 p q r s t u v w x y z { | } ~ DEL
U+0080 ƒ ˆ Š Œ Ž
U+0090 ˜ š œ ž Ÿ
U+00A0 ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯
U+00B0 ° ± ² ³ ´ µ · ¸ ¹ º » ¼ ½ ¾ ¿
U+00C0 À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï
U+00D0 Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß
U+00E0 à á â ã ä å æ ç è é ê ë ì í î ï
U+00F0 ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ


Unicode (UTF-16) では,半角カナは $FF60 ~ $FF9F の領域にあります.半角カナの小文字は $FF67 ~ $FF6F にあります.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
U+FF60
U+FF70 ソ
U+FF80
U+FF90


[備考 1]
半角カタカナ (半角カナ) は,コンピュータで処理可能な文字が 1 バイトだけだった時代,日本語を扱うために考えられ,作られた文字です.濁点と半濁点を別の文字としました.これは画期的なことでした.
コンピュータの性能が上がり,漢字が扱えるようになりましたが,互換性のため,半角カタカナも使用できるようになっています.現在では,半角カタカナを使用する必然性は全くありません.


[備考 2]
アルファベットとローマ字はほとんど同じ意味で使われることがあります.
アルファベットは,英語で使用されている A, B, C, .. a, b, c, .. などの文字のことです.
ローマ字は,日本における独特の呼称で,「ラテン語の文字」という意味でですが,アルファベットを用いて日本語の発音を表記する意味でも使用されています.


[備考 3]
一般的な文書ででは,ASCII コードの $21 ~ $7E に相当する文字は半角を使用します.カタカナは全角を使用します.
当サイトのウェブページでは等幅フォントを使用していますが,一般的な文書ではプロポーショナルフォントを使用します.


[備考 4]
「半角」というのは,漢字の半分の幅で表示されるという意味と思われますが,プロポーショナルフォントを使用した場合は,必ずしも漢字の半分の幅にはなりません.半角文字と全角文字の区別がつきにくい表示になります.漢字の半分の幅になるのは等幅フォントを使用した場合です.





02_コードポイントと文字 / 文字列 ( Ord 関数と Char 関数 )


文字は,画面に表示したり,コントロールに描画したり,プリンタで印刷すると文字になります.メモリ上やディスク内ではバイナリデータとして存在します.そのバイナリデータが,その文字のコードポイントの値です.

文字のコードポイントの値を取得するには Ord 関数を使用します.コードポイントの値を文字とするには Char 関数を使用します.コードポイントの値を文字にするということは,バイナリデータを単なる数値ではなく,コードポイントの値として処理するようにすることです.これは,数値の先頭に # を追加するのと同じことです.
以下は,コードポイントで文字を処理するために必要な,Ord 関数と Char 関数の使用例です.



図5
文字のコードポイント
図6
文字列の各文字のコードポイント

リスト1
//=============================================================================
//  コードポイントと文字
//  Unicode ( UTF-16 )
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LChar : Char;
  LCode : Cardinal;
begin
  Memo1.Lines.Clear;

  LChar := 'あ';
  Memo1.Lines.Add(LChar);

  // 3042 は あ のコードポイント ( $ を付ければ 16 進数 )
  // # を付ければ文字として Char に代入可能
  LChar := #$3042;
  Memo1.Lines.Add(LChar);
  //
  LChar := Char($3042);
  Memo1.Lines.Add(LChar);

  // LCode は あ のコードポイントの値になる
  // Ord('あ') でも同じ
  LCode := Ord(LChar);

  LChar := Char(LCode);
  Memo1.Lines.Add(LChar);
end;

//=============================================================================
//  コードポイントと文字列
//  Unicode ( UTF-16 )
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LStr   : string;
  LIndex : Integer;
  LCode  : Cardinal;
begin
  Memo1.Lines.Clear;

  LStr := '日本語';
  Memo1.Lines.Add(LStr);

  LStr := #$65E5 + #$672C + #$8A9E;
  Memo1.Lines.Add(LStr);

  LStr := #$65E5#$672C#$8A9E;
  Memo1.Lines.Add(LStr);

  LStr := Char($65E5) + Char($672C) + Char($8A9E);
  Memo1.Lines.Add(LStr);

  // 文字列の各文字のコードポイントの値をリスト
  for LIndex := 1 to Length(LStr) do begin
    LCode := Ord(LStr[LIndex]);
    Memo1.Lines.Add('$' + IntToHex(LCode, 4));
  end;
end;




03_半角カタカナの小文字を大文字に変換


文字列中内の半角カタカナの小文字を,半角カタカナの大文字に変換するサンプルです.
半角カタカナの小文字には,ァィゥェォャュョッ の 9 文字あります.
文字入力の時,[F8] キーを押すと,半角のカタカナになります.[Shift] キーを押した状態で [F8] を押すと,半角カタカナの小文字になります.



図7
実行結果

リスト2
//=============================================================================
//  半角カナの小文字を半角カナの大文字に変換
//  Unicode の場合
//  Unicode では半角カナの小文字は $FF67 から $FF6F の 9 文字
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  CharArray : array [$FF67..$FF6F] of Char
      = (#$FF71, #$FF72, #$FF73, #$FF74, #$FF75, #$FF94, #$FF95, #$FF96, #$FF82);
var
  Ltext  : string;
  LCode  : Integer;
begin
  LText := 'アア ァィゥェォャュョッ 日本語';

  Memo1.Lines.Clear;
  Memo1.Lines.Add(Ltext);

  for LCode := Low(CharArray) to High(CharArray) do begin
    Ltext := StringReplace(Ltext, Char(LCode), CharArray[LCode], [rfReplaceAll]);
  end;
  Memo1.Lines.Add(LText);
end;

//=============================================================================
//  半角カナの小文字を半角カナの大文字に変換
//  Shift_JIS ( コードページは 932 ) の場合
//  Shift_JIS では半角カナの小文字は $A7 から $AF の 9 文字
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
const
  CharArray : array [$A7..$AF] of AnsiChar
     = (#$B1, #$B2, #$B3, #$B4, #$B5, #$D4, #$D5, #$D6, #$C2);
var
  LUniStr  : string;
  LAnsiStr : AnsiString;
  LCode    : Byte;
  LIndex   : Integer;
begin
  LUniStr  := 'アア ァィゥェォャュョッ 日本語';
  LAnsiStr := AnsiString(LUniStr);

  Memo1.Lines.Clear;
  Memo1.Lines.Add(string(LAnsiStr));

  LIndex := 1;
  while LIndex <= Length(LAnsiStr) do begin

    LCode := Ord(LAnsiStr[LIndex]);
    if AnsiChar(LCode) in LeadBytes then begin
      Inc(LIndex, 2);
      Continue;
    end;

    if LCode in [Low(CharArray)..High(CharArray)] then begin
      LAnsiStr[LIndex] := CharArray[LCode];
    end;
    Inc(LIndex);
  end;
  Memo1.Lines.Add(String(LAnsiStr));
end;


以下は,Ansi 版の Delphi 用のサンプルコードです.Ansi 版の Delphi とは,Delphi 6 / Delphi 7 / Delphi 2007 などのことです.



リスト3
//=============================================================================
//  半角カナの小文字を半角カナの大文字に変換
//  Ansi 版の Delphi 用のコード
//  Delphi 6 / 7 / 2007 は Andi 版の Delphi
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
procedure TForm1.Button1Click(Sender: TObject);
const
  CharArray : array [$A7..$AF] of Char
       = (#$B1, #$B2, #$B3, #$B4, #$B5, #$D4, #$D5, #$D6, #$C2);
var
  LText  : string;
  LCode  : Byte;
  LIndex : Integer;
begin
  LText := 'アア ァィゥェォャュョッ 日本語';

  Memo1.Lines.Clear;
  Memo1.Lines.Add(LText);

  LIndex := 1;
  while LIndex <= Length(LText) do begin

    LCode := Ord(LText[LIndex]);
    if Char(LCode) in LeadBytes then begin
      Inc(LIndex, 2);
      Continue;
    end;

    if LCode in [Low(CharArray)..High(CharArray)] then begin
      LText[LIndex] := CharArray[LCode];
    end;
    Inc(LIndex);
  end;

  Memo1.Lines.Add(LText);
end;




04_半角カタカナの小文字を大文字に変換 - 文字単位で処理


元のテキストの文字単位で処理する例です.


リスト4
//=============================================================================
//  半角カナの小文字を半角カナの大文字に変換
//  Unicode の場合.文字単位で処理
//  Unicode では半角カナの小文字は $FF67 から $FF6F の 9 文字
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  CharArray : array [$FF67..$FF6F] of Char
      = (#$FF71, #$FF72, #$FF73, #$FF74, #$FF75, #$FF94, #$FF95, #$FF96, #$FF82);
var
  LText  : string;
  LCode  : Integer;
  LIndex : Integer;
begin
  LText := 'アア ァィゥェォャュョッ 日本語';

  Memo1.Lines.Clear;
  Memo1.Lines.Add(LText);

  for LIndex := 1 to Length(LText) do begin
    LCode := Ord(LText[LIndex]);
    if (LCode >= Low(CharArray)) and (LCode <= High(CharArray)) then begin
      LText[LIndex] := CharArray[LCode];
    end;
  end;
  Memo1.Lines.Add(LText);
end;




05_半角カタカナの大文字を小文字に変換


最初のサンプルとは逆に,半角カタカナの大文字を小文字に変換るサンプルです.
半角カタカナの大文字のコードポイントは連続した値ではありません.そこで,このサンプルでは,半角カタカナの大文字のコードポイントの値を配列要素の値にして処理しています.



図8
半角カナの大文字を小文字に変換

リスト5
//=============================================================================
//  半角カナの大文字を半角カナの小文字に変換
//  Unicode の場合
//  Unicode では半角カナの小文字は $FF67 から $FF6F の 9 文字
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  CharArray : array [0..8] of Char
      = (#$FF71, #$FF72, #$FF73, #$FF74, #$FF75, #$FF94, #$FF95, #$FF96, #$FF82);
var
  Ltext  : string;
  LIndex : Integer;
  LChar  : Char;
begin
  LText := 'アァ アイウエオヤユヨツ 日本語';

  Memo1.Lines.Clear;
  Memo1.Lines.Add(Ltext);

  for LIndex := Low(CharArray) to High(CharArray) do begin
    LChar := CharArray[LIndex];
    Ltext := StringReplace(Ltext, LChar, Char(LIndex + $FF67), [rfReplaceAll]);
  end;
  Memo1.Lines.Add(LText);
end;




06_JIS コードのテキストの場合


JIS コードのコードページは,50220, 50221, 50222 の 3 つかあります.
以下は,コードページが 50221 の JIS コードのテキストの処理例です.JIS コード以外のコードページの場合にも応用できます.
コードページが 50221 のテキストファイルを Unicode (UTF-16) に変換して読み込みます.その UniCode のテキストの半角カタカナの小文字を半角カタカナの大文字にします.処理したらコードページ 50221 のテキストとして保存します.

Unicode に変換してから処理しますから,本ページの最初のサンプルのコードが利用できます.


図9
コードページが 50221 のテキストの場合
  • 順に
  • 読み込んだテキスト
  • 半角カナの小文字を大文字に変換
  • それを保存して,再度読み出して表示

[備考 1]
GetEncoding には,TEncoding.GetEncoding('ISO-2022-JP') のように文字列 ISO-2022-JP (大文字小文字は不問) を指定できます.ただし,この指定で保存した場合は,コードページは 50220 になります.読み込むと時は,コードページを自動で認識してくれます.


リスト6
//=============================================================================
//  コードページ 50221 の文字列の半角カナの小文字を大文字に変換
//  コードページの変換に TEncoding.GetEncoding を使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  CharArray : array [$FF67..$FF6F] of Char
      = (#$FF71, #$FF72, #$FF73, #$FF74, #$FF75, #$FF94, #$FF95, #$FF96, #$FF82);
var
  LFilePath : string;
  LSavePath : string;
  LCodePage : Integer;
  LStrList  : TStringList;
  LDstStr   : string;
  LCode     : Integer;
begin
  LFilePath := '..\50221.txt';
  LSavePath := 'convJIS.txt';
  LCodePage := 50221;

  // JIS コードのテキストファイルを読み込む
  LStrList := TStringList.Create;
  try
    LStrList.LoadFromFile(LFilePath, TEncoding.GetEncoding(LCodePage));
    Memo1.Lines.Text := LStrList.Text;

    // 半角カナの小文字を半角カナの大文字に変換
    LDstStr := LStrList.Text;
    for LCode := Low(CharArray) to High(CharArray) do begin
      LDstStr := StringReplace(LDstStr, Char(LCode), CharArray[LCode], [rfReplaceAll]);
    end;
    Memo1.Lines.Add(Trim(LDstStr));

    // 変換後のテキストを JIS コードにしてファイルに保存
    LStrList.Text := LDstStr;
    LStrList.SaveToFile(LSavePath, TEncoding.GetEncoding(LCodePage));


    // 保存したテキストを読み込んで表示
    // 確認のために,保存したテキストを読み込んで表示
    LStrList.Clear;
    LStrList.LoadFromFile(LSavePath, TEncoding.GetEncoding(LCodePage));
    Memo1.Lines.Add(LStrList.Text);
  finally
    FreeAndNil(LStrList);
  end;
end;


同じ処理を,MECSUtils の関数類を使用して行うサンプルです.
このコードであれば,Delphi 6 / Delphi7 でも動作します (確認済み).


リスト7
implementation   

uses
  MECSUtils;

{$R *.dfm}

//=============================================================================
//  コードページ 50221 の文字列の半角カナの小文字を大文字に変換
//  コードページの変換に MECSUtils の関数類を使用
//
//  uses に MECSUtils が必要 
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  CharArray : array [$FF67..$FF6F] of WideChar
      = (#$FF71, #$FF72, #$FF73, #$FF74, #$FF75, #$FF94, #$FF95, #$FF96, #$FF82);
var
  LFilePath : string;
  LsavePath : string;
  LCodePage : Cardinal;
  LStream   : TMemoryStream;
  LRawStr   : ByteString;
  LDstStr   : WideString;
  LCode     : Integer;
begin
  LFilePath := '..\50221.txt';
  LSavePath := 'convJIS.txt';
  LCodePage := 50221;

  LStream := TMemoryStream.Create;
  try
    // JIS コードのテキストファイルを読み込む
    LStream.LoadFromFile(LFilePath);
    SetLength(LRawStr, LStream.Size);
    LStream.Read(LRawStr[1], LStream.Size);

    // 読み込んだテキストを Unicdoe に変換
    Mecsutils.ConvertMultiByteToUnicode(LCodePage, LRawStr, LDstStr);
    Memo1.Lines.Text := LDstStr;

    // 半角カナの小文字を半角カナの大文字に変換
    for LCode := Low(CharArray) to High(CharArray) do begin
      LDstStr := StringReplace(LDstStr, WideChar(LCode), CharArray[LCode], [rfReplaceAll]);
    end;
    Memo1.Lines.Add(LDstStr);

    // 変換後のテキストを JIS コードにしてファイルに保存
    LRawStr := '';
    LStream.Clear;
    Mecsutils.ConvertUnicodeToMultiByte(LCodePage, LDstStr, LRawStr);
    LStream.Write(LRawStr[1], Length(LRawStr));
    LStream.SaveToFile(LSavePath);


    // 確認のために,保存したテキストを読み込む
    LStream.LoadFromFile(LSavePath);
    SetLength(LRawStr, LStream.Size);
    LStream.Read(LRawStr[1], LStream.Size);

    // 読み込んだテキストを Unicdoe に変換
    Mecsutils.ConvertMultiByteToUnicode(LCodePage, LRawStr, LDstStr);
    Memo1.Lines.Add(LDstStr);
  finally
    FreeAndNil(LStream);
  end;
end;


コードページが 50222 のテキストファイルを読み込んで TMemo に表示した結果です.筆者の環境では,下図のように文字化けします.


図10
コードページ 50222 のテキストの文字化け
  • 半角カナの小文字がなければ文字化けしない

リスト8
//=============================================================================
//  コードページ 50222 のテキストを読み込んで表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
  LCodePage : Integer;
  LStrList  : TStringList;
begin
  LFilePath := '..\50222.txt';
  LCodePage := 50222;

  LStrList := TStringList.Create;
  try
    LStrList.LoadFromFile(LFilePath, TEncoding.GetEncoding(LCodePage));
    Memo1.Lines.Text := LStrList.Text;
  finally
    FreeAndNil(LStrList);
  end;
end;


[備考 2]
TStringList.LoadFromFile(FilePath, TEncoding.GetEncoding(50221)) でテキストファイルを読み込んだ場合,文字列は,Unicode (UTF-16) の文字列になります.したがって,読み込んだ文字列のバイトデータ (バイナリデータ) は,保存されている文字列のバイトデータ (バイナリデータ) と同じではありません.
TMemoryStream.LoadFromFile(FilePath) で読み込んだ場合は,保存されている文字列のバイトデータ (バイナリデータ) をそのまま読み込みます.

TStringList.LoadFromFile でテキストファイルを読み込む時,TEncoding でコードページを指定しない場合,保存されている文字列のバイトデータ (バイナリデータ) を Shift_JIS のコードポイントのデータと解釈して読み込み,自動的に Unicode (UTF-16) に変換します.
同じ文字であっても,コードページが異なると,その文字のコードポイントは異なります.このことは,後掲の「全角ひらがなと全角カタカナのコードポイント」のサンプルで確認できます.このコードポイントの違いが,いわゆる「文字化け」が発生する主な原因です.

メモリやディスクにに書き込まれているのは「文字」ではなく,コードポイントとしてのバイトデータ (バイナリデータ) です.現在普及しているコンピュータでは,実際に文字,テキストを記憶したり保存することはできません.以下のページは,このことに関する説明記事となっています.





07_テスト用の JIS コードのテキストファイル作成


テストに使用したテキストファイルは,以下のコードで作成しました.
JIS コードのテキストファイルは,メモ帳では正常に表示できません.文字化けします.今回は,秀丸エディタというテキストエディタを使用して,これらのテキストファイルのテキストが文字化けなしで表示できることを確認しました.


リスト9
//=============================================================================
//  JIS コードのテキストファイルを作成
//  コードページは 50220, 50221, 50222 の 3 種類ある
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
type
  Jis50220String = type AnsiString(50220);
  Jis50221String = type AnsiString(50221);
  Jis50222String = type AnsiString(50222);
var
  LText     : string;
  L50220Str : Jis50220String;
  L50221Str : Jis50221String;
  L50222Str : Jis50222String;
  LStream   : TMemoryStream;
begin
  LText := 'アア ァィゥェォャュョッ 日本語';


  L50220Str := Jis50220String(LText);
  LStream := TMemoryStream.Create;
  try
    LStream.Write(L50220Str[1], length(L50220Str));
    LStream.Position := 0;
    LStream.SaveToFile('..\50220' + '.txt');
  finally
    FreeAndNil(LStream);
  end;


  L50221Str := Jis50221String(LText);
  LStream := TMemoryStream.Create;
  try
    LStream.Write(L50221Str[1], length(L50221Str));
    LStream.Position := 0;
    LStream.SaveToFile('..\50221' + '.txt');
  finally
    FreeAndNil(LStream);
  end;


  L50222Str := Jis50222String(LText);
  LStream := TMemoryStream.Create;
  try
    LStream.Write(L50222Str[1], length(L50222Str));
    LStream.Position := 0;
    LStream.SaveToFile('..\50222' + '.txt');
  finally
    FreeAndNil(LStream);
  end;
end;


上のコードで作成すれば,各文字の文字コード (コードポイント) の値を確認する処理を追加したい場合に便利です.コードポイントの値を確認する必要がなければ,TEncoding.GetEncoding を使用して,保存する時にコードページを指定することがてきます.


リスト10
//=============================================================================
//  JIS コードのテキストファイルを作成
//  コードページは 50220, 50221, 50222 の 3 種類ある
//  TEncoding.GetEncoding を使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrList : TStringList;
begin
  LStrList := TStringList.Create;
  try
    LStrList.Text := 'アア ァィゥェォャュョッ 日本語';

    LStrList.SaveToFile('..\50220.txt', TEncoding.GetEncoding(50220));
    LStrList.SaveToFile('..\50221.txt', TEncoding.GetEncoding(50221));
    LStrList.SaveToFile('..\50222.txt', TEncoding.GetEncoding(50222));
  finally
    FreeAndNil(LStrList);
  end;
end;




08_コードページ 50221 ( JIS コード ) の文字のバイトデータ


JIS コードのコードページは,50220, 50221, 50222 の 3 つかあります.
コードページが 50221 の時のテキストのバイト値は次のようになります.
これは,全角のカタカナの小文字の「ァ」と大文字の「ア」の場合です.$25 $21 が「ァ」の JIS コードで,$25 $22 が「ア」の JIS コードです.それ以外は $1B で始まるエスケープシーケンスと呼ばれるバイト列です.$1B は,ASCII 文字では [ESC] です.
エスケープシーケンスの値によって,どの文字集合 (JIS 規格の文字) を使用するかを示します.文字集合には,NEC 特殊文字,NEC 選定 IBM 拡張文字,IBM 拡張文字も含まれます.


図11
JIS コードの文字列
  • コードページは 50221
  • $25 $21 が小文字の ァ の JIS コード
  • $25 $22 が大文字の ア
  • 最終行はバイト値相当の ASCII 文字

半角と全角の文字の両方がある場合は次のようになります.
$31 が半角の ァ の JIS コードで,$25 $22 が全角の ア の JIS コードです.

各々のコードページの詳細については,以下のリンクの記事を参考にしてください.コードページが 50220 のテキス
トは,半角のカタカナは扱えません.半角のカタカナは,全角のカタカナに置き換わります.



図12
半角と全角の文字がある場合
  • コードページは 50221
  • $31 が半角の ア の JIS コード
  • $25 $22 が全角の ア

//=============================================================================
//  コードページ 50221 のテキストの JIS コードの例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
type
  Jis1String = type AnsiString(50221);
var
  LJisStr   : Jis1String;
  LByte     : Byte;
  LTextByte : string;
  LTextChar : string;
  LIndex    : Integer;
begin
  LJisStr := 'ァア';

  Memo1.Lines.Clear;
  Memo1.Lines.Add(string(LJisStr));

  LTextByte := '';
  LTextChar := '';
  for LIndex := 1 to Length(LJisStr) do begin
    LByte := Ord(LJisStr[LIndex]);
    LTextByte := LTextByte + '$' + IntToHex(LByte, 2) + ' ';
    LTextChar := LTextChar + ' ' + Char(LByte) + '  ';
  end;

  Memo1.Lines.Add(TrimRight(LTextByte));
  Memo1.Lines.Add(TrimRight(LTextChar));
end;




09_全角ひらがなと全角カタカナのコードポイント


Unicode (UTF-16) では,全角ひらがなは $3041 ~ $3096 に,全角カタカナは $30A1 ~ $30F6 にあります.全角のひらがなとカタカナも,コードポイント (文字コード) の書き換えという考え方で処理できますが,全角のひらがなとカタカナは,下表のように,半角とは違い,小文字が連続していません.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
U+3040
U+3050
U+3060
U+3070
U+3080
U+3090
U+30A0
U+30B0
U+30C0
U+30D0
U+30E0
U+30F0


以下は,全角のひらがなとカタカナのコードポイントの値を列挙するサンプルです.全角のひらがなの小文字と全角のカタカナの小文字には,半角のカタカナの小文字にはない「ゎ,ゕ,ゖ,ヮ,ヵ,ヶ」の小文字ががあります.ひらがなの小文字「ゕ,ゖ」は,Shift_JIS, JIS にはありません.

このサンプルで分かるように,一般的に,同じ「文字」であっても,コードページが異なれば,コードポイントの値も異なります.$00 ~ $7F の,ASCII コードの文字は,コードページに関係なく,数値としては同じです.例えば,%00A1 は,2 バイトを意味しますが,数値としては $A1 と同じです.



図13
全角のひらがなとカタカナのコードポイント
  • Unicode (UTF-16) の場合
  • コードページ
    1200 はリトルエンディアン
    1201 はビックエンディアン
  • 全角も半角も 2 バイト
図14
全角のひらがなとカタカナのコードポイント
  • Shift_JIS (AnsiString) の場合
  • コードページは 932
  • Shift_JIS では,
    全角は 2 バイト
    半角は 1 バイト
図15
全角のひらがなとカタカナのコードポイント
  • コードページが 50221 (JIS) の場合
  • コードページ 50221 では,
    全角は 2 バイト
    半角は 1 バイト
図16
全角のひらがなとカタカナのコードポイント
  • UTF-8 の場合
  • コードページは 65001
  • UTF-8 の文字は 1 から 4 バイトの可変長

リスト11
implementation

uses
  System.WideStrUtils;

{$R *.dfm}

//=============================================================================
//  全角ひらがなと全角カタカナのコードポイント一覧
//  Unicode ( UTF-16 ) の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LText : string;
  LCode : Integer;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  for LCode := $3041 to $30F6 do begin
    LText := '$' + IntToHex(LCode, 4) + '   ' + Char(LCode);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;

//=============================================================================
//  全角ひらがなと全角カタカナのコードポイント一覧
//  Shift_JIS の場合
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LCode    : Integer;
  LAnsiStr : AnsiString;
  LText    : string;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  SetLength(LAnsiStr, 2);

  for LCode := $829F to $8396 do begin
    LAnsiStr[1] := AnsiChar((LCode and $FF00) shr 8);
    LAnsiStr[2] := AnsiChar(LCode and $FF);

    LText := '$' + IntToHex(LCode, 4) + '   ' + string(LAnsiStr);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;

//=============================================================================
//  全角ひらがなと全角カタカナのコードポイント一覧
//  コードページが 50221 ( JIS コード ) の場合
//
//  JIS コードだけでは文字にならない ( 文字として表示されない )
//  JIS コードの前に $1B2442 を,後ろに $1B2842 を追加する必要がある
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
type
  JisString = type AnsiString(50221);
var
  LCode    : Integer;
  LJisStr  : JisString;
  LText    : string;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  SetLength(LJisStr, 8);
  LJisStr[1] := #$1B;
  LJisStr[2] := #$24;
  LJisStr[3] := #$42;
  LJisStr[6] := #$1B;
  LJisStr[7] := #$28;
  LJisStr[8] := #$42;

  for LCode := $2421 to $2576 do begin
    LJisStr[4] := AnsiChar((LCode and $FF00) shr 8);
    LJisStr[5] := AnsiChar(LCode and $FF);

    LText := '$' + IntToHex(LCode, 4) + '   ' + string(LJisStr);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;

//=============================================================================
//  全角ひらがなと全角カタカナのコードポイント一覧
//  UTF-8 の場合
//
//  uses に System.WideStrUtils が必要
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
  LCode    : Integer;
  LUTF8Str : UTF8String;
  LText    : string;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  SetLength(LUTF8Str, 3);

  for LCode := $E38181 to $E383B6 do begin
    LUTF8Str[1] := AnsiChar((LCode and $FF0000) shr 16);
    LUTF8Str[2] := AnsiChar((LCode and $FF00) shr 8);
    LUTF8Str[3] := Ansichar(LCode and $FF);

    // 末尾バイトが UTF-8 として有効でない場合は次の処理とする
    if not IsUTF8TrailByte(LUTF8Str[3]) then Continue;

    LText := '$' + IntToHex(LCode, 6) + '   ' + string(LUTF8Str);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;


Unicode (UTF-16) の場合,次のように,ループ変数の初期値と最終値に自体的な文字を使用して処理することができます.



リスト12
//=============================================================================
//  全角ひらがなと全角カタカナのコードポイント一覧
//  Unicode ( UTF-16 ) の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LText : string;
  LCode : Integer;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  for LCode := Ord('ぁ') to Ord('ヶ') do begin
    LText := '$' + IntToHex(LCode, 4) + '   ' + Char(LCode);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;

//=============================================================================
//  全角ひらがなと全角カタカナのコードポイント一覧
//  Shift_JIS の場合
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LCode      : Integer;
  LAnsiStr   : AnsiString;
  LFirstCode : Integer;
  LLastCode  : Integer;
  LText      : string;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;


  LAnsiStr := 'ぁ';
  LFirstCode := (Ord(LAnsiStr[1]) shl 8) + Ord(LAnsiStr[2]);
  LAnsiStr := 'ヶ';
  LLastCode := (Ord(LAnsiStr[1]) shl 8) + Ord(LAnsiStr[2]);

  for LCode := LFirstCode to LLastCode do begin
    LAnsiStr := AnsiChar(Byte(Swap(LCode))) + AnsiChar(Byte(LCode));
    LText := '$' + IntToHex(LCode, 4) + '   ' + string(LAnsiStr);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;




10_全角カタカナの小文字を大文字に変換


前項のサンプルで全角のカタカナのコードポイントの値を知ることができます.それらの値を使用して,全角カタカナの小文字を大文字に変換する例です.
Unicode と Shift_JIS 以外は,Unicode の文字列にして変換すると処理が簡単になります.Unicode の文字列に変換すると言っても,ここまでのサンプルのように,単に Unicode の文字列変数に代入するだけです.ファイルから読み込む例もすでに掲載しています.



図17
全角カタカナの小文字を大文字に変換
  • Unicdoe (UTF-16) の場合

リスト13
//=============================================================================
//  全角カタカナの小文字を大文字に変換
//  Unicode ( UTF-16 ) の場合
//  全角のカナの小文字には,半角カナの小文字の他に ヮ,ヵ,ヶ がある
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  LCodes : array [0..11] of Cardinal
= ($30A1, $30A3, $30A5, $30A7, $30A9, $30C3, $30E3, $30E5, $30E7, $30EE, $30F5, $30F6);
var
  LText  : string;
  LCode  : Integer;
  LChar  : Char;
  LIndex : Integer;
begin
  Memo1.Lines.Clear;

  LText := 'ア ァィゥェォャュョッヮヵヶ 日本語';
  Memo1.Lines.Add(LText);

  for LIndex := 0 to High(LCodes) do begin
    if LIndex = (High(LCodes) - 1) then begin
      LCode := $30AB;
    end else
    if (LIndex = High(LCodes)) then begin
      LCode := $30B1;
    end else begin
      LCode := LCodes[LIndex] + 1;
    end;
    LChar := Char(LCodes[LIndex]);
    Ltext := StringReplace(Ltext, LChar, Char(LCode), [rfReplaceAll]);
  end;
  Memo1.Lines.Add(LText);
end;




11_全角カタカナの大文字を小文字に変換


上のサンプルとは逆に,全角のカタカナの大文字を小文字に変換するサンプルです.



図18
全角カタカナの大文字を小文字に変換
  • Unicdoe (UTF-16) の場合

リスト14
//=============================================================================
//  全角カタカナの大文字を小文字に変換
//  Unicode ( UTF-16 ) の場合
//   全角のカナの小文字には,半角カナの小文字の他に ヮ,ヵ,ヶ がある
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  LCodes : array [0..11] of Cardinal
= ($30A2, $30A4, $30A6, $30A8, $30AA, $30C4, $30E4, $30E6, $30E8, $30EF, $30AB, $30B1);
var
  LText  : string;
  LCode  : Integer;
  LChar  : Char;
  LIndex : Integer;
begin
  Memo1.Lines.Clear;

  LText := 'ァ アイウエオヤユヨツワカケ 日本語';
  Memo1.Lines.Add(LText);

  for LIndex := 0 to High(LCodes) do begin
    if LIndex = (High(LCodes) - 1) then begin
      LCode := $30F5;
    end else
    if (LIndex = High(LCodes)) then begin
      LCode := $30F6;
    end else begin
      LCode := LCodes[LIndex] - 1;
    end;
    LChar := Char(LCodes[LIndex]);
    Ltext := StringReplace(Ltext, LChar, Char(LCode), [rfReplaceAll]);
  end;
  Memo1.Lines.Add(LText);
end;




12_全角のカタカナを全角のひらがなに変換


全角のカタカナを全角のひらがなに変換するサンプルです.ただし,テキスト内の文字ではなく,単純に全角のカタカナに相当するひらがなを列挙するだけとなっています.
このサンプルには以下のコードがあります.これは全角カタカナのコードポイントと,全角ひらがなのコードポイントの値がどれだけ離れた位置 (ポイント) にあるかを示します.この値を利用して変換します.


  // $30A1 は全角カタカナの「ァ」のコードポイントの値
  // $3041 は全角ひらがなの「ぁ」のコードポイントの値
  LIncCode := $30A1 - $3041;


コンピュータのメモリやディスク内には,実際の「文字」は存在しません.コンピュータのデータは全てバイナリです.その結果,本サンプルのような処理が可能となるわけです.
英数字 (アルファベットや数字) の全角半角の相互変換も,同様の方法で可能です.Unicode (UTF-16) では,半角も全角も,コードポイントの値は 2 バイトです.



図19
全角カタカナに相当する全角ひらがなの一覧
  • Unicdoe (UTF-16) の場合

リスト15
//=============================================================================
//  全角カタカナを全角のひらがなに変換
//  Unicode ( UTF-16 ) の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIncCode : Integer;
  LText    : string;
  LIndex   : Integer;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  // $30A1 は全角カタカナの「ァ」のコードポイントの値
  // $3041 は全角ひらがなの「ぁ」のコードポイントの値
  LIncCode := $30A1 - $3041;

  for LIndex := $30A1 to $30F6 do begin
    LText := Char(LIndex) + '  ' + Char(LIndex - LIncCode);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;


全角のひらがなは全角のカタカナに,全角のカタカナは全角の平仮名に変換する例です.



図20
全角のひらがなとカタカナの変換

リスト16
//=============================================================================
//  全角カタカナは全角のひらがなに,ひらがなはカタカナに変換
//  Unicode ( UTF-16 )
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LText    : string;
  LCode    : Integer;
  LIncCode : Integer;
  LIndex   : Integer;
begin
  LText := 'ABC ひらがな カタカナ 日本語';

  Memo1.Lines.Clear;
  Memo1.Lines.Add(LText);

  LIncCode := $30A1 - $3041;

  for LIndex := 1 to Length(LText) do begin
    LCode := Ord(LText[LIndex]);

    if (LCode >= $3041) and (LCode <= $3096) then begin
      LText[LIndex] := Char(LCode + LIncCode);
    end else
    if (LCode >= $30A1) and (LCode <= $30F6) then begin
      LText[LIndex] := Char(LCode -+ LIncCode);
    end;
  end;

  Memo1.Lines.Add(LText);
end;




13_全角の英数字のコードポイントと全角と半角への変換


Unicdoe (UTF-16) の半角英数字は,%0021 ~ $007E にあります.半角の英数字のコードポイントは,数値としては ASCII コードと同じです.これらは ASCII 文字と呼ばれています.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
U+0020   ! " # $ % & ' ( ) * + , - . /
U+0030 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
U+0040 @ A B C D E F G H I J K L M N O
U+0050 P Q R S T U V W X Y Z [ \ ] ^ _
U+0060 ` a b c d e f g h i j k l m n o
U+0070 p q r s t u v w x y z { | } ~ DEL

Unicode (UTF-16) の全角の英数字関係の記号と英数字は,$FF01 ~ $FF5E にあります.
これらの文字は,半角の文字,つまり,ASCII 文字と順番が同じです.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
U+FF00
U+FF10
U+FF20
U+FF30 _
U+FF40
U+FF50


半角文字を全角文字にしたり,全角文字を半角文字にするには Windows API の CMapString 関数が利用できます.しかし,この関数は,記号や英数字もカナ文字も全角にしたり半角にします.英字 (アルファベット) だけ,あるいは数字だけを変換の対象にするには,個別に処理することになります.
そのためなは,それらのコードポイントを知る必要があります.コードポイントが分かれば,前項のサンプルのように,「コードポイントの距離」を使用して変換できます.
以下は,コードポイントを調べるためのサンプルです.



図21
全角と半角の英数字のコードポイント
  • これは全角のアルファベットの場合
  • アルファベットには小文字もある

リスト17
//=============================================================================
//  記号と半角数字のコードポイント一覧
//  Unicode ( UTF-16 ) の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LText : string;
  LCode : Integer;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  for LCode := Ord('!') to Ord('9') do begin
    LText := '$' + IntToHex(LCode, 4) + '   ' + Char(LCode);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;

//=============================================================================
//  記号と半角英字 ( アルファベット ) のコードポイント一覧
//  Unicode ( UTF-16 ) の場合
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LText : string;
  LCode : Integer;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  for LCode := Ord(':') to Ord('~') + 1 do begin
    LText := '$' + IntToHex(LCode, 4) + '   ' + Char(LCode);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;

//=============================================================================
//  記号と全角数字のコードポイント一覧
//  Unicode ( UTF-16 ) の場合
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LText : string;
  LCode : Integer;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  for LCode := Ord('!') to Ord('9') do begin
    LText := '$' + IntToHex(LCode, 4) + '   ' + Char(LCode);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;

//=============================================================================
//  記号と全角英字 ( アルファベット ) のコードポイント一覧
//  Unicode ( UTF-16 ) の場合
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
  LText : string;
  LCode : Integer;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;

  for LCode := Ord(':') to Ord('~') + 1 do begin
    LText := '$' + IntToHex(LCode, 4) + '   ' + Char(LCode);
    Memo1.Lines.Add(LText);
  end;

  Memo1.Lines.EndUpdate;
end;


半角のアルファベットだけを全角のアルファベットに変換する例です.
記号やアルファベットも数字も,このサンプルのように,半角と全角の文字のコードポイントの差を使用して,半角と全角の相互変換ができます.上の表は全角の英数字のコードポイントの値です.これらの半角文字のコードポイントは $0020 ~ $007E ($20 ~ $7E でも同じ) にあります.これらの文字のコートポイントの差は常に $FF01 - $0021 です.

Unicode (UTF-16) の空白文字については,以下の記事を参考にしてください.Shift_JIS の半角空白文字は $20 で,全角空白文字は $8140 です.



図22
半角のアルファベットを全角に変換

リスト18
//=============================================================================
//  半角英字を全角の英字に変換
//  Unicode ( UTF-16 ) の場合
//
//  $002C は半角の .
//  $002E は半角の ,
//  . と , は数字小数点,桁区切りにも使用されるている
//  記号,英数字共に変換する場合は含めてもよいだろう
//
//  $0020 は半角の空白.$3000 は全角の空白
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIncCode : Integer;
  LText    : string;
  LCode    : Integer;
  LIndex   : Integer;
begin
  Memo1.Lines.Clear;
  LIncCode := $FF01 - $0021;

  LText := 'Delphi XE5';
  Memo1.Lines.Add(LText);

  for LIndex := 1 to Length(LText) do begin
     LCode := Ord(LText[LIndex]);
    if (LCode = $002C) or (LCode = $002E)
      or ((LCode >= $0041) and (LCode <= $007A)) then begin
      LText[LIndex] := Char(LCode + LIncCode);
    end;
    if LCode = $0020 then begin
      LText[LIndex] := Char($3000);
    end;
  end;
  Memo1.Lines.Add(LText);
end;


全角の数字を半角の数字に変換する例です.カンマなどの記号も変換します.
全角のアルファベットや全角カタカナは半角には変換しません.



図23
全角の数字を半角に変換

リスト19
//=============================================================================
//  全角の数字を半角の数字に変換 ( 記号類も含めて )
//  Unicode ( UTF-16 )
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIncCode : Integer;
  LText    : string;
  LCode    : Integer;
  LIndex   : Integer;
begin
  Memo1.Lines.Clear;
  LIncCode := $FF01 - $0021;

  LText := 'ABC 123,456.001 アイウガパ';
  Memo1.Lines.Add(LText);

  for LIndex := 1 to Length(LText) do begin
     LCode := Ord(LText[LIndex]);

    if (LCode >= $FF01) and (LCode <= $FF1F) then begin
      LText[LIndex] := Char(LCode - LIncCode);
    end;
  end;

  Memo1.Lines.Add(LText);
end;


コードページが 932 の Shift_JIS の場合,全角の 0 から 9 だけの変換であれば問題ありません.上のサンプルのように,カンマなども変換しいた場合は少し面倒です.
Unicode (UTF-16) では,英数字関係の記号と英数字のコードポイントは,ASCII 文字のコードと同じ順番になっています.この仕様により,コードポイントの差を計算するだけで全角と半角の変換ができます.

下表は,Shift_JIS のコードポイントです.英数字関係の記号類も,ひらがなやカタカナで使用する記号類も同じ領域にあります.数字の部分は ASCII 文字と並び順が同じですが,記号の部分は ASCII 文字と同じ順番ではありません.Shift_JIS の場合は,カンマなどの記号と数字とで処理を分ける必要があります.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
8140   ´ ¨
8150 _
8160
8170 ± ×

16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
8240
8250


[備考]
一般的な文書では,全角の英数字は使われていません.





14_半角カタカナを全角カタカナに変換


文字列内の半角カタカナだけを全角カタカナにするサンプルです.
Unicode (UTF-16) では,半角カナは $FF60 ~ $FF9F の領域にあります.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
U+FF60
U+FF70 ソ
U+FF80
U+FF90


Unicode (UTF-16) の全角カタカナで使用する文字は,$30A1 ~ $30FF にあります.
全角のひらがなとカタカナも,半角と全角の英数字も互いにコードポイントが連続していますが,半角と全角のカタカナは並び順が同じではありません.変換テーブルを作成するのは面倒なので, LCMapString 関数を利用することにします.


16 進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
U+30A0
U+30B0
U+30C0
U+30D0
U+30E0
U+30F0


文字列全体を LCMapString 関数で全角に変換すると,アルファベットや数字の半角も全角になってしまいます.そこで,半角のカタカナの文字だけを LCMapString 関数で変換しています.

以下のリンク記事では,LCMapString 関数を使用して,半角に変換可能かをチェックしてから,再度 LCMapString 関数を実行して変換しています.半角に変換できないのは,純粋なカナではなく,カナ文字の領域にある記号類の一部です.
半角カタカナから全角カタカナへの変換の場合は,変換できい文字はないので,以下のサンプルではその処理を省略しています.
Unicode (UTF0-16) の場合,単独の文字としての濁点記号と半濁点記号は,別の記号に変換されてしまうので個別に処理しています (コードポイントが 1 つ違う値になる).



図24
半角カタカナを全角カタカナに変換

リスト20
//=============================================================================
//  文字列の半角カタカナだけを全角カタカナに変換
//  Unicode ( UTF-16 ) の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSrcText : string;
  LDstText : string;
  LSrcStr  : string;
  LDstStr  : string;
  LCode    : Integer;
  LCode2   : Integer;
  LIndex   : Integer;
begin
  Memo1.Lines.Clear;

  LSrcText := 'ABC アイウ「ガギグ」パピプ 123 日本語';
  Memo1.Lines.Add('$' + IntToHex(Ord(LSrcText[Length(LSrcText)]), 4));
  Memo1.Lines.Add(LSrcText);

  LDstText := '';
  LIndex := 1;

  while LIndex <= Length(LSrcText) do begin
    LCode  := Ord(LSrcText[LIndex]);;
    LSrcStr := LSrcText[LIndex];

    if (LCode >= $FF61) and (LCode <= $FF9D) then begin
      if (LIndex + 1) <= Length(LSrcText) then begin
        LCode2 := Ord(LSrcText[LIndex + 1]);
        if ((LCode2 = $FF9E) or (LCode2 = $FF9F)) then begin
          LSrcStr := LSrcStr + Char(LCode2);
        end;
      end;
      SetLength(LDstStr, 1);
      LCMapString(LOCALE_SYSTEM_DEFAULT, LCMAP_FULLWIDTH,
                  PChar(LSrcStr), Length(LSrcStr), PChar(LDstStr), Length(LDstStr));
    end else

    // $FF9E は半角の濁点記号.$FF9F は半角の半濁点記号
    if LCode = $FF9E then begin
      LDstStr := Char($309B);
    end else
    if LCode = $FF9F then begin
      LDstStr := Char($309C);

    // 半角の空白を全角の空白に ( 必要なければ下の 3 行を削除 )
    end else
    if LCode = $0020 then begin
      LDstStr := Char($3000);
    end else begin
      LDstStr := LSrcStr;
    end;

    LDstText := LDstText + LDstStr;
    Inc(LIndex);
    if Length(LSrcStr) = 2 then Inc(LIndex);
  end;

  Memo1.Lines.Add(LDstText);
end;


同じく,文字列内の半角カタカナだけを全角カタカナにするサンプルです.
以下は,Ansi 版の Delphi 用のサンプルコードです.Ansi 版の Delphi とは,Delphi 6 / Delphi 7 / Delphi 2007 などのことです.



図25
半角カタカナを全角カタカナに変
  • Delphi 6 での実行結果

リスト21
//=============================================================================
//  文字列の半角カタカだけナを全角カタカナに変換
//  Shift_JIS の場合 ( Ansi 版 Delphi 用のコード )
//  Memo1 (TMemo ) の Font.Charset はデフォルトの SHIFTJIS_CHARSET
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSrcText : string;
  LDstText : string;
  LSrcStr  :string;
  LDstStr  :string;
  LCode    : Integer;
  LCode2   : Integer;
  LIndex   : Integer;
begin
  Memo1.Lines.Clear;

  LSrcText := 'ABC アイウ「ガギグ」パピプ 123 日本語';
  Memo1.Lines.Add(LSrcText);

  LDstText := '';
  LIndex := 1;

  while LIndex <= Length(LSrcText) do begin
    LCode   := Ord(LSrcText[LIndex]);;
    LSrcStr := LSrcText[LIndex];

      if Char(LCode) in LeadBytes then begin
        if (LIndex + 1) <= Length(LSrcText) then begin
          LSrcStr := LSrcStr + LSrcText[LIndex + 1];
          LDstStr := LSrcStr;
        end;
    end else
    if (LCode >= $A1) and (LCode <= $DF) then begin
      if (LIndex + 1) <= Length(LSrcText) then begin
        LCode2 := Ord(LSrcText[LIndex + 1]);
        if (LCode <= $DD) and ((LCode2 = $DE) or (LCode2 = $DF)) then begin
          LSrcStr := LSrcStr + Char(LCode2);
        end;
      end;
      SetLength(LDstStr, 2);
      LCMapString(LOCALE_SYSTEM_DEFAULT, LCMAP_FULLWIDTH,
                  PChar(LSrcStr), Length(LSrcStr), PChar(LDstStr), Length(LDstStr));

    // 半角の空白を全角の空白に ( 必要なければ下の 3 行を削除 )
    end else
    if LCode = $20 then begin
      LDstStr := Char($81) + Char($40);
    end else begin
      LDstStr := LSrcStr;
    end;

    LDstText := LDstText + LDstStr;
    Inc(LIndex);
    if Length(LSrcStr) = 2 then Inc(LIndex);
  end;

  Memo1.Lines.Add(LDstText);
end;