カタカナ(全角)を数値化するには?

解決


tama  2003-11-28 00:06:45  No: 5797

ア,イ,ウ,エ,オ → 11,12,13,14,15
カ,キ,ク,ケ,コ → 21,22,23,24,25
--------------------------------
--------------------------------
ラ,リ,ル,レ,ロ → 91,92,93,94,95
ワ             → 96
ン             → 99

のようにカタカナ(全角)を数値化するにあたって、
if文を47本書いています。
他に方法はありますか?


Halbow  2003-11-28 00:51:28  No: 5798

Halbow です。

全角のカナ一文字と数値が対応づけられればいいのですね。
TStrings.Values[] を使って「連想配列」にすると任意の数値と対応づけられます。
詳しくはヘルプで調べてください。ア行についてだけの例を示します。

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    SL:TStringList;
    function KanaToInt(ZenKana:string):integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  SL := TStringList.Create;
  SL.Add('ア=11');
  SL.Add('イ=12');
  SL.Add('ウ=13');
  SL.Add('エ=14');
  SL.Add('オ=15');
  // とりあえずア行だけためす
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SL.Free;
end;

function TForm1.KanaToInt(ZenKana: string): integer;
begin
  result := StrToInt(SL.Values[ZenKana]);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  s,t:string;
  ws:WideString;
  i:integer;
begin
  s := 'イエオアウ';
  ws := s;
  t := '';
  for i := 1 to Length(ws) do
    t := t + IntToStr(kanaToInt(ws[i]))+' ';
  Label1.Caption := t;
end;


tama  2003-11-28 03:36:20  No: 5799

Halbowさん、素早いresponseを有難うございました。
エレガントさには感動しました。
例示して頂きましたコードも理解でき、世界が広がった気がします。


XOOX  2003-11-29 07:58:26  No: 5800

解決したようですが
基本的に日本語自体がコードです。
わざわざstringListを使うのは無駄です。

カタカナはJIS05区でSJISだと$8340から$8396の連続した86個の値です。
例えばアは$8341になります。

もしも任意の数字にしたい場合は86個の配列を用意して
文字を2バイトのデータとして扱い
コードから$8340を引いて対応値を取得するほうがよいと思います。


Halbow  2003-11-29 11:12:29  No: 5801

Halbow です。

> もしも任意の数字にしたい場合は86個の配列を用意して
> 文字を2バイトのデータとして扱い
> コードから$8340を引いて対応値を取得するほうがよいと思います。

そうですね。この方が「連想配列」より高速だと思います。わたしも最初はこのように考えて、文字コード表を見てみたのですが、規則正しくアイウエオと並んでいないのですね。最初に代入するときが面倒なのが欠点だと言えるでしょう。

ちょっとア行についてやってみました。

  public
    function KanaToInt(ZenKana:string):integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  KanaArr:array[0..86] of integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  KanaArr[1] := 11;
  KanaArr[3] := 12;
  KanaArr[5] := 13;
  KanaArr[7] := 14;
  KanaArr[9] := 15;
  // とりあえずア行だけためす
end;

function TForm1.KanaToInt(ZenKana: string): integer;
begin
  result := KanaArr[(Ord(ZenKana[1]) shl 8) + Ord(ZenKana[2]) - $8340];
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  s,t:string;
  ws:WideString;
  i:integer;
begin
  s := 'イエオアウ';
  ws := s;
  t := '';
  for i := 1 to Length(ws) do
    t := t + IntToStr(kanaToInt(ws[i]))+' ';
  Label1.Caption := t;
end;


Halbow  2003-11-29 11:43:42  No: 5802

Halbow です。

> 最初に代入するときが面倒なのが欠点だと言えるでしょう。

上の欠点を改善しました。これの方がいいです。XOOX さん、ありがとうございます。

  public
    function GetCode(ZenKana:string):integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  KanaArr:array[$8340..$8396] of integer;

function TForm1.GetCode(ZenKana: string): integer;
begin
  result := Ord(ZenKana[1]) shl 8 + Ord(Zenkana[2]);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  KanaArr[GetCode('ア')] := 11;
  KanaArr[GetCode('イ')] := 12;
  KanaArr[GetCode('ウ')] := 13;
  KanaArr[GetCode('エ')] := 14;
  KanaArr[GetCode('オ')] := 15;
  // とりあえずア行だけためす
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  s,t:string;
  ws:WideString;
  i:integer;
begin
  s := 'イエオアウ';
  ws := s;
  t := '';
  for i := 1 to Length(ws) do
    t := t + IntToStr(kanaArr[GetCode(ws[i])])+' ';
  Label1.Caption := t;
end;


XOOX  2003-11-29 22:30:16  No: 5803

思うにこういったものの代入はプログラムでやるよりも
データベースとかCSVの読込みでやる方が良いように感じます。
又根幹にはJISコードがあるので一区あたりのサイズとして$94を推奨します。
又出力は1byteでよいんじゃないでしょうか。
今回のケースは、つまり
KanaArr:array[$8340..$8396] of integer;
ではなくて
KanaArr:array[$8340..$8396] of byte;

JIS05_Arr:array[$00..$93] of word;
というのが最も筋が通っているように思います。
細かい突っ込みのように思われるかもしれませんが
物事には成り立ちと経緯というものがありそれを無視して
プログラムを組むのは非効率であると思うのです。

参考までに05区の並びは以下の通りです。
ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダ
チヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミム
メモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶ

const文で組んじゃったほうが速いかな?
以降についてはHalbowさんの手順でよいと思いますが
目的は数値化であって数値化からの文字化ではないように思うので
例えば
a:string;b:byteとして
a:='ア';
b:=Byte(a[3])-$40;
求める値一つについてJIS05_Arr[b]で良いと思います。
カタカナだけの文字列の場合一つ飛ばしで1バイト取るというのがミソです。

この先は文字変換全般に突入しちゃうので複雑化する必要も無いと思います。


Halbow  2003-11-30 01:12:17  No: 5804

Halbow です。

> 思うにこういったものの代入はプログラムでやるよりも
> データベースとかCSVの読込みでやる方が良いように感じます。

それは実際はそうでしょう。でもそれは今回の質問の本質ではありませんね。
質問に対する回答の論理が具体的に示されれば、あとのことはまた別の問題です。

> 細かい突っ込みのように思われるかもしれませんが
> 物事には成り立ちと経緯というものがありそれを無視して
> プログラムを組むのは非効率であると思うのです。

そうですね。わたしは現在の整数の汎用型である integer がもっとも自然だと思いますよ。処理も速いですし。もともとユーザ指定の数値なので byte や word にする意味はないと思います。

> 例えば
> a:string;b:byteとして
> a:='ア';
> b:=Byte(a[3])-$40;
> 求める値一つについてJIS05_Arr[b]で良いと思います。
> カタカナだけの文字列の場合一つ飛ばしで1バイト取るというのがミソです。

うーむ、これは論理の汎用化ということでは同意できません。文字コードから任意の数値に対応付けるのが今回の質問の核心です。わざわざ処理を複雑にして一般性を失ってもコードが読みにくく、得られることは少ないでしょう。a[3] はゼロだと思います。a[2] ですよね。全角カナは、一バイト目が共通なのでいらない、ということでしょうが。


XOOX  2003-12-01 00:00:52  No: 5805

>a[3] はゼロだと思います。

上の例の場合D7は知りませんがD6は添字は1からです。従って2になります。
たまたまこれはstringを使い適切な表現をしたからこうなったまでです。
当然Delphiでの動作確認もしています。

処理を複雑化にしていると思われていますが
ストリームで読み込むときに一つおきに読み込むということで
単に一つおきによむということです。
確かにintegerは汎用レジスタですが文字処理で特殊処理の目的でなく使うというのは問題があります。

コンパイラはWordやbyteとintegerでは処理速度が違ってきます。
ちょっとアセンブラをいじればすぐにわかるでしょう。
又使用データ範囲も違ってきます。
論理の汎用化というのは結局は存在しないに等しいと思うし
スペックをより大きく取ってしまうものです。

又「プログラム」の中で一つ一つ描写しないと実装できないHalbowさんの方法より
ストリーム内での読込みを前提にしてbyte単位での読み込み処理をする私の方法の方がつぶしがききます。

なぜinteger配列にアクセスして
Ord(ZenKana[1]) shl 8 + Ord(Zenkana[2]);
とする方法が
Pbyte(Pinteger(@ZenKana[1])+3)^的な私の方法より汎用性がありシンプルだと思うのか
つまるところHalbowさんはポインタベースでのメモリアクセスの発想がない
という一言に尽きるのではないでしょうか?

無論Halbowさんが同意する必要はありません。
遅くとも自分のスタイルに合ったものの方が全体のコーディング効率があがるからです。

私は現実問題として
「フォントを必要最低限ロードして日本語表示環境を実現するための動的テーブル」
ということがカレントな作業なのでこの話題はかなりリアルだったものですから


Halbow  2003-12-01 00:36:17  No: 5806

Halbow です。

> 遅くとも自分のスタイルに合ったものの方が全体のコーディング効率があがるからです。

そうですね。遅いとか速いとかは、もちろん重要な要因ではありますけど、ここのような掲示板での質問に対しては、論理がはっきり示せるようなコードで回答することが重要です。質問者が回答の論理を理解して、そのうえでさらに処理の高速化を望むのでしたら、それはまた別の質問にすべきです。

それから、ストリームについてはこれまでに言及がないので、ここでいきなり持ち出すのはどうしてですか? 上の回答でも配列だったのでは?
わたしはポインタが大好きです。しかし、掲示板での回答は、質問者にできるだけ分かりやすくするため、また、論理をはっきり示すため、なるだけポインタを使用しないようにしています。わたしの HP や ML で質問者のスキルが分かっているときは、ポインタを使いまくりです。(笑)メモリストリームが最近のお気に入りです。

わたしの発想の仕方を論評するより、掲示板での質問への回答の書き方がわたしと XOOX さんでは違うということでしょう。わたしは、最高の論理を示すより、つねに具体的で分かりやすい方の書き方を選びます。


XOOX  2003-12-01 02:52:37  No: 5807

わかりました。
その前にByte(a[3])-$40;はやはりByte(a[2])-$40;の間違いでした。
もうしわけありません。

しかし「具体的で分かりやすい方の書き方」という点において
明らかに間違っていた点はきちんと認識お願いいたします。
少なくともHalbowさんの方法だと何か変更があるとプログラマーに言って
全部コードを打ち直してもらわないとだめなコードが生成されるという点において
プログラムのパーツかとチェックの手間がかかるという点において
まずいコーディングの典型になってしまっている事をご認識ください。

とにもかくにもプログラムで一行一行配列に代入するコードが
いかに不適切かということをまず学んでください。

私があえてJISコード順に「全ての」カタカナをコード順に記述した点に注目してください。
質問者は書き込みのその部分をコピペして変数なり定数に代入するだけで
自分の好きなコーディングを組むことができますし言語依存度も低いです。

つまりHalbowさんの書き込みはHalbowさん独自のコーディング方法による解決で
Halbowさんにとってはそれでいいだろうし
一つのかいだとは思いますが
私のは「世間の常識」レベルの取るに足りない代物だということです。

何をもって質問者のスキルとかいうのかわからないですが
文字コードの仕様を前提にしたのが私の書込みであり
ろくに調べもしないでの反射的反論や修正は
むしろ質問者にわかりにくくなるので避けていただきたいように思います。
少なくとも「全角カタカナ」の処理への回答で当初「カタカナ」とは何かすら知らなかったことは
否定できない事実だと思います。


XOOX  2003-12-01 04:44:07  No: 5808

最初の質問に対する解がHalbowさんじたいごちゃついたので
とりあえずシンプルな例を書きます。
基本的にはhalbowさんの2番目の書き込みの改善程度です。

値の決まっているものの配列化の場合はこういう書き方がdelphiの場合
一般的だと思います。
Halbowさんが「とりあえず」といって5文字だけ取上げたのは
delphiの代入機構の難点を現していて
基本的には静的定数でなければ外部ファイル読込みを推奨します。

関数に渡すのはAnsistring一文字です。

根本的に最初のtamaさんの発想自体すでに濁点を無視しており不適切ですが
とりあえず濁点、半濁点などは99いこうを返す形にしました。
無論変更は容易です。
変数名などは混乱を避ける為halbowさんに合わせました。
//この定数部分は変更の可能性があれば変数にして読み込みルーチンを使うか
//$Iでテキストファイルで独立させるのも手
const
KanaArr: array[0..$94] of integer =(
16,11,17,12,18,13,19,14,20,15,21,26,22,27,23,28,24,29,25,30,
31,36,32,37,33,38,34,39,35,40,41,46,42,47,90,43,48,44,49,45,50,
51,52,53,54,55,61,56,66,62,57,67,63,58,68,64,59,69,65,60,70
71,72,73,74,75,86,81,87,82,88,83,
91,92,93,94,95,76,96,77,78,98,99,79,80,89
);
//
//ヤユヨは81,82,83とした 
(*全角カタカナすべて
ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダ
チヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミム
メモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶ
//*)

function GetCode(ZenKana: string): integer;
begin
  result := KanaArr[(Byte(ZenKana[2])-$40)];
end;


XOOX  2003-12-01 04:50:46  No: 5809

]ゴミはご容赦
訂正
function GetCode(ZenKana: string): integer;
begin
  result := KanaArr[(Byte(ZenKana[2])-$40);
end;

あくまで一文字のカタカナを数値化する関数です。
用法とデータ入力法がわからないのでサンプル例はかきませんでした。


Halbow  2003-12-01 07:53:16  No: 5810

Halbow です。

> とにもかくにもプログラムで一行一行配列に代入するコードが
> いかに不適切かということをまず学んでください。

質問の本質は、代入の仕方ではありませんよ。連想配列だって、ファイルから読み込めます。回答の意図は代入の仕方をしめすことではありません。それから、'ァ' に 16 を割り当てるのは、質問の意図と無関係です。なるべく簡単で明確に論理をしめす方法をまず学んでください。

それから、連想配列より文字コードから数値に変換する方がよいことにはもちろん同意致します。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加