全角文字を変数から一文字ずつ抽出するには?

解決


Delphi大好き  2009-08-01 01:49:16  No: 35319

以下の様な、検索の為に使う半角→全角変換ソフト
(最終的には、検索に埋め込む関数にする予定)を作ってます。

環境は、WinXP SP3 TurboDelphi2006(無償版)です。
(ただ、Delphi3(OS:VistaビジネスSP1)でも使うことを前提に作ってます。)

半角→全角は問題無く動くまで出来たのですが、
全角を入力すると、s2{中身・} := sp^[i2{中身1}]{中身あ};
の部分が誤作動して、文字化けがおきてしまいます。

なので、全角文字を一文字ずつ識別して、
そこだけ変換せずにスルーする方法を探しています。

今の所、一文字づつ全角文字を抽出する方法が判らず、
立ち往生しています。
何か良い方法は無いでしょうか?

お知恵をお貸し頂けると助かります。
それではよろしくお願いしますm(__)m。

var
  Form1: TForm1;
  PS: pchar;

//...省略

function allocpchar(s: string): pchar;
begin
  ps := stralloc(length(s) + 1);
  allocpchar := strpcopy(ps, s);
end;

//...省略
procedure TForm1.Button1Click(Sender: TObject);
var
  i1,i2,i3,i4:integer;
  s1,s2,s3,s4,s5:string;
  sp:^string;
begin
  i1 := length(edit1.Text) + 1;
  s1 := edit1.Text;
  sp := @s1;
  s4 := '';
  i2 := 1;
  i3 := 0;
  i4 := Ord('ン');

  while i1 > i2 do begin
    s2 := sp^[i2];
    while i4 > i3 do begin
      s3 := allocpchar(char(i3));
      case sp^[i2] of
        '0': s5 := '0';
        '1': s5 := '1';
        '2': s5 := '2';
        '3': s5 := '3';
        '4': s5 := '4';
        '5': s5 := '5';
        '6': s5 := '6';
        '7': s5 := '7';
        '8': s5 := '8';
        '9': s5 := '9';

        //...省略
      end;
      if s2 = s3 then begin
        s4 := s4 + s5;
        edit1.text := s4;
      end;
      i3 := i3 + 1;
    end;
    i2 := i2 + 1;
    i3 := 0;
  end;
end;


DEKO  2009-08-01 02:08:26  No: 35320

発想の転換でこんなのは如何でしょうか?

[半角を全角に変換する]
http://www.wwlnk.com/boheme/delphi/tips/tec1310.htm

どうしても自前でやりたければ、SysUtils.LeadBytes変数を使って、

if (s[i] in LeadBytes) then

のように、漢字の1バイト目かどうかを判断しながら処理を行うといいと思います。


D  2009-08-01 03:51:44  No: 35321

自前でやる場合の一つの案ということで。
文字列をWideStringで扱うとマルチバイトの1バイト目かどうかというような判断をする必要がなくて楽です。

procedure Form1.Button1Click(Sender: TObject);
var
  i: Integer;
  ls_Src: WideString;
begin
  ls_Src := WideString(Edit1.Text);

  for i := 1 to Length(ls_Src) do begin
    case (ls_Src[i]) of
      '0': ls_Src[i] := WideString('0')[1];
      '1': ls_Src[i] := WideString('1')[1];
      '2': ls_Src[i] := WideString('2')[1];
      '3': ls_Src[i] := WideString('3')[1];
      '4': ls_Src[i] := WideString('4')[1];
      '5': ls_Src[i] := WideString('5')[1];
      '6': ls_Src[i] := WideString('6')[1];
      '7': ls_Src[i] := WideString('7')[1];
      '8': ls_Src[i] := WideString('8')[1];
      '9': ls_Src[i] := WideString('9')[1];

      //...省略
    end;
  end;

  Edit1.Text := ls_Src;
end;

この場合、いちいちWideStringでキャストして添え字をつけないといけないのがわずらわしですが。

あと、LCMapStringは'\'だけは全角に変換されないのでLCMapStringで変換後さらにStringReplaceなどを使って'\'を'¥'に変換しないとなりません。

https://www.petitmonte.com/bbs/answers?question_id=5611


DEKO  2009-08-01 04:01:56  No: 35322

> 自前でやる場合の一つの案ということで。
> 文字列をWideStringで扱うとマルチバイトの1バイト目かどうかというような判断をする必要がなくて楽です。

そうですね。ラウンドトリップ問題を無視できる場合にはDさんの方法が楽でいいです。

[Ansi->Unicode->Ansi変換をやっちゃ駄目ってどういう事?]
http://homepage1.nifty.com/ht_deko/tech013.html#tech039_20


Delphi大好き  2009-08-01 05:52:37  No: 35323

DEKOさん、Dさんありがとうございます。
PCのセットアップが落ち着いたので、レスにきました。

驚くほど短い文の方法があるんですね。
凄い勉強になります。

Dさんの方法も、とても参考になります。

Delphi3での動作も問題なく、色々使えそうです。

一日で解決するとは思いませんでした。
困った時はAPIですね^^。
Vistaの件と変換の件、本当にありがとうございましたm(__)m。

明日早速関数にして組み込みたいと思います。
DEKOさんDさん、重ねてありがとうございました。


M  2009-08-01 05:55:28  No: 35324

参考までにお聞きしたいのですが、

http://www.wwlnk.com/boheme/delphi/tips/tec1310.htm

の方法って、「¥」の全角、半角切り替えがうまくいかないように思えます。

これを含めて置換できる方法って何かありましたっけ?
自分では「AnsiPos」で「¥」を見つけて全角半角入れ代えをやってます。


DEKO  2009-08-01 11:14:55  No: 35325

> これを含めて置換できる方法って何かありましたっけ?
Dさんが仰っていたように、LCMapString 後に
S := Stringreplace(S, '\', '¥', [rfReplAll]);
すればいいと思います。


DEKO  2009-08-01 12:22:41  No: 35326

誤: S := Stringreplace(S, '\', '¥', [rfReplAll]);
正: S := Stringreplace(S, '\', '¥', [rfReplaceAll]);


Delphi大好き  2009-08-01 22:27:23  No: 35327

皆さんありがとうございます、
>S := Stringreplace(S, '\', '¥', [rfReplaceAll]);
を改良していくと、全角→半角にも出来そうなので、
そちらの方も作りはじめました。

試しで一文字目の変換が成功した所です。
Dさんの方法も参考にしながら、
この調子で組み上げていきたいです。

半角→全角はDEKOさんのおかげで簡単に成功しました。
改めてありがとうございました。


Delphi大好き  2009-08-02 00:36:53  No: 35328

自己レスです。

procedure TForm1.Button3Click(Sender: TObject);
var
  s,s2:string;
  i:integer;
begin
  s := edit3.Text;
  s2 := '';
  for i := 1 to Length(s) do begin
    if (s[i] in LeadBytes) then  begin

      if s[i] + s[i+1] = '0' then begin
        s2 := s2 + '0';
        if (s[i+2] in LeadBytes) then begin
          //s2 := s2 +s[i+2]+s[i+3];
        end else begin
          s2 := s2 + s[i+2];
        end;
      end;
      if s[i] + s[i+1] = '1' then begin
        s2 := s2 + '1';
        if (s[i+2] in LeadBytes) then begin
          //s2 := s2 +s[i+2]+s[i+3];
        end else begin
          s2 := s2 + s[i+2];
        end;
      end;
      //...省略
    end;
  end;
  edit3.Text := s2;
end;

全角→半角変換の試作も無事、出来ました。
あとはコピー&ペーストしながら文字を登録して、出来たら移植用に仕上げるだけです。

ほぼ全部何とかなりました。

ありがとうございましたm(__)m。


D  2009-08-02 09:28:51  No: 35329

老婆心ながら、、。

わかっててやってるのだったらごめんなさい。

>全角→半角変換の試作も無事、出来ました。
>あとはコピー&ペーストしながら文字を登録して、出来たら移植用に仕上げるだけです。

LCMapString APIは半角を全角にするだけでなく、全角を半角にすることもできます。
それだけでなく大文字⇔小文字、ひらがな⇔カタカナなどの変換もできます。
他にもエンディアンの変換など色々できるようです。
http://msdn.microsoft.com/ja-jp/library/cc448052.aspx


Delphi大好き  2009-08-02 22:07:25  No: 35330

>Dさん

情報ありがとうございます、
助かります^^。
全く知りませんでした^^;
半角→全角だけだと思っていました。

使っている市販の業務用請求書ソフトに、そのような機能があり、
それに近づけたいと思っていました。

どうやって変換してるのだろう?と興味津々でしたが、
APIにそういう機能があったのですね^^;

そうとも知らず、昨日は上のを書いた後、
動作を調べたら案の定バグバグで苦労してました^^;

リンクもありがとうございます、
早速APIの参考書と一緒に早速調べてみます(API超初心者です^^;)。
ありがとうございました。


Fusa  2009-08-03 19:36:13  No: 35331

参考にしてください。

APIをラップした関数群やAPIに依存しない変換を行っています。

http://delfusa.main.jp/delfusafloor/opensource/delfusalibrary/20070828160200/StringUnit/StringUnitLight.pas.txt


Delphi大好き  2009-08-04 23:07:49  No: 35332

>Fusaさん

情報ありがとうございます。
早速見てみたのですが、自分のスキルを大幅に超えていて、
全然判りませんでした^^;

でも貴重な情報ありがとうございましたm(__)m。
学習を兼ねて、切り貼りしながら参考にしてみたいと思います。


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

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






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