Delphi Tips 
-----------------------------

キーワード:文字列

>> Index

03/04 自作アプリにスペルチェックの機能をつけたい
03/04 正規表現の使える検索・置換えライブラリ
08/22 プロパティ値を文字列に変換/逆変換
05/17 HTMLタグ表記の大文字・小文字変換を行う
05/17 文字列から括弧の中のみを削除する
05/17 列挙型(Enum)の値と文字列との変換
05/17 文字列や画像データをリソースに埋め込むためのコンポーネント
12/27 VBのMIDステートメント
06/02 トークンの切り出し
09/16 Pascal で文字列を効率良く扱う(例:文字列を逆順にする)。
09/15 漢数字で位取り表示
09/09 文字列を TDateTime に変換する
08/13 TColor 値を文字列に変換する
05/14 文字数のカウント方法
05/08 改行コードの違いについて
02/11 和暦を西暦に直したい
02/08 Delphi3 の TStringList.CommaText の不具合
02/08 文字列の切り分け

最終更新: 5104 日前

0046  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.2
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2004/03/04 osamu 編集
自作アプリにスペルチェックの機能をつけたい

Spell Checker(辞書付き)というコンポーネントが、http://www.cs.monash.edu.au/~vtran/にあります。
Delphian World MLの過去ログにこれに関する話題があるそうです。
http://www.delphianworld.com/
参照: [Delphi-ML:19276] <PASCAL>

0119  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.2
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2004/03/04 osamu 編集
正規表現の使える検索・置換えライブラリ

正規表現を使った文字列探索/操作 : Rexp016.lzh
Perl 互換正規表現ユニット : BRegExp.lzh

DLは Delphian World で、
http://www.delphianworld.com/direct.html?id=MI0036

それか、この手のDLLがいろいろありますからそれらを使うという手がラクだと思います。

VWXW.DLL(Wz Editorで使われている)
作者のM.Ishidaさんのページ
http://www2h.meshnet.or.jp/~ishida/

jre.dll(秀丸エディタで使われている)
http://www.yamada-labs.com/software/spec/jre/

basp21.dll
http://www.hi-ho.ne.jp/babaq/
参照: [Delphi-ML:22272] <PASCAL>

0346  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 2003/08/22 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2003/08/22 osamu 編集
プロパティ値を文字列に変換/逆変換

published なプロパティを Ini に書き込みたい・から読み出したいというような時、プロパティ値を文字列に変換する必要が生じます。

Font プロパティを Ini/Registry に保存したい という投稿はFAQに近いですが、この記事はそういった要望への回答となります。

Enum/Set だけではなく、TColor のように「Enum ではないけど文字で書きこまれることがあるプロパティ」にも対応しています。

uses Classes, TypInfo;

function PropertyToString(obj: TPersistent; PropName: string): string;
var IntToIdent: TIntToIdent;
    Ident: string;
begin
  Result:= GetPropValue(obj, PropName);
  if PropType(obj, PropName)=tkInteger then begin
    IntToIdent := FindIntToIdent(GetPropInfo(obj, PropName).PropType^);
    if Assigned(IntToIdent) then
      IntToIdent(GetPropValue(obj, PropName), Result);
  end;
end;

procedure StringToProperty(s: string; obj: TPersistent; PropName);
var IdentToInt: TIdentToInt;
    i: Integer;
begin
  if PropType(obj, PropName)==tkInteger then begin
    IdentToInt:= FindIdentToInt(GetPropInfo(obj, PropName).PropType^);
    if Assigned(IdentToInt) and IdentToInt(s, i) then begin
      SetPropValue(obj, PropName, i);
    end else begin
      SetPropValue(obj, PropName, s);
    end;
  end else begin
    SetPropValue(obj, PropName, s);
  end;
end;
参照: [Delphi-ML:75958] <PASCAL>

0326  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 2002/05/17 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2002/05/17 osamu 編集
HTMLタグ表記の大文字・小文字変換を行う

コードはSJISを仮定します。

HTMLのコメント部分、""などで囲まれたパラメータ部分などは変換しません。

速度を気にするのであれば IsDBCSLeadByte は避けるべきです。自分で文字テーブルを作れば速くなります。ByteType の方は最適化されていたと思います。通常の用途では速度が気になることは無いかもしれませんね。

function ChangeHTMLTagCase(s:string; Lower:Boolean):string;
const
    sNormal   = 0;
    sInComment= 1;
    sInBracket= 2;
    sInQuote  = 3;
    sInDQuote = 4;
    sAfterEq  = 5;
var
  p: PChar;
  state: Integer;
begin
  state:= 0;
  Result:= s;
  UniqueString(Result);
  p:= PChar(Result);
  while p^<>#0 do begin
    if IsDBCSLeadByte(Ord(p^)) and ((p+1)^<>#0) then begin
      Inc(p, 2);
    end else begin
      case state of
      sNormal:
        if p^='<' then
          if StrLComp(p, '<!--', 4)=0
            then state:= sInComment
            else state:= sInBracket;
      sInComment:
        if p^='-' then
          if StrLComp(p, '-->', 3)=0 then
            state:= sNormal;
      sInBracket:
        case p^ of
        '''': state:= sInQuote;
        '"':  state:= sInDQuote;
        '=':  state:= sAfterEq;
        '>':  state:= sNormal;
        'A'..'Z': if Lower     then p^:= Chr(Ord(p^)+32);
        'a'..'z': if not Lower then p^:= Chr(Ord(p^)-32);
        end;
      sInQuote:
        case p^ of
        '''': state:= sInBracket;
        end;
      sInDQuote:
        case p^ of
        '"':  state:= sInBracket;
        end;
      sAfterEq:
        case p^ of
        '''': state:= sInQuote;
        '"':  state:= sInDQuote;
        ' ':  state:= sInBracket;
        '>':  state:= sNormal;
        end;
      end;
      Inc(p);
    end;
  end;
end;
参照: [Delphi-ML:66840] <WWW> <通信> <PASCAL>

0325  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 2002/05/17 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2002/05/17 osamu 編集
文字列から括弧の中のみを削除する

特に、全角の括弧と半角の括弧に対応した方法を紹介します。一旦WideStringに直しているところが味噌で、こうすることによってコードの見通しが非常に良くなります。

function DeleteInBracket(s:string):string;
const
  ZenAki = $FF08;
  ZenToji= $FF09;
  HanAki = $0028;
  HanToji= $0029;
var
  PrevPos,Len,BTimes,i:integer;
  ss:WideString;
begin
  ss := s;
  i := 1; PrevPos := 1;Len := Length(ss);BTimes := 0;
  result := '';
  while i < Len+1 do begin
    case Ord(ss[i]) of
      ZenAki,HanAki:begin
        Inc(BTimes);
        if BTimes = 1 then result := result+Copy(ss,PrevPos,i-PrevPos);
      end;
      ZenToji,HanToji:begin
        Dec(BTimes);
        if BTimes = 0 then PrevPos := i+1;
      end;
    end;
    inc(i);
    If BTimes < 0 then Break;
  end;
  if BTimes = 0 then
    result := result+Copy(ss,PrevPos,i-PrevPos)
  else if BTimes < 0 then begin
    ShowMessage('カッコが閉じていません-- ( が足りません' );
    result := result+Copy(ss,PrevPos,i-PrevPos);
  end else begin
    ShowMessage('カッコが閉じていません--  ) が足りません' );
  end;
end;

また、こういった文字列処理には正規表現を使うとかなり楽ができます。たとえば、
http://www2.big.or.jp/~osamu/Delphi/MyLibrary.htm
の BRegExp ユニットを使えば、

uses BRegExp;

function RemoveParentheses(text: string): string;
begin
  Result:= text;
  while brx.Subst('s/(\(|()[^()()]{3,1024}(\)|))//gk', Result) do;
end;

とするだけでできてしまいます。
s/// の k というオプションは、BREGEXP.DLL に固有のもので、正規表現中の SJIS を SJIS として認識してくれるので、2バイト文字にも特殊な処理はいらないです。
参照: [Delphi-ML:66865] <PASCAL>

0324  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 2002/05/17 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2002/05/17 osamu 編集
列挙型(Enum)の値と文字列との変換

TBrushStyle や TPenStyle などの値を文字列として取り出したい、またはそのような文字列からもとの値に戻したい場合、TypInfoユニットにあるGetEnumNameやGetEnumValueを使用します。

uses TypInfo;

function AlignmentToString(Value: TAlignment): String;
begin
  Result := GetEnumName(TypeInfo(TAlignment),Ord(Value));
end;

こんな感じですね。
参照: [Delphi-ML:66981] <PASCAL>

0320  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 2002/05/17 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2002/05/17 osamu 編集
文字列や画像データをリソースに埋め込むためのコンポーネント

プログラム内部で使いたい文字列や画像のデータを、フォーム上に置いた非ビジュアルコンポーネントに保存しておくと、アプリケーションと別にファイルを配布する必要が無くなり、便利な場合があります。

そういった用途に使えるコードが[Delphi-ML:67285]のスレッドに紹介されています。ただし、画像データなどは文字列にエンコードされますので、そのままファイルを持つ場合に比べ、ファイルサイズは大きくなってしまいます。

[Delphi-ML:50664]で中村さんが紹介されたコンポーネントは任意のバイナリデータを内部に持てるようです。
参照: [Delphi-ML:67285] <画像> <PASCAL>

0313  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 2001/12/05 濱野 rev 1.5
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2001/12/27 濱野 編集
VBのMIDステートメント

Delphi6でVB互換の関数などが追加されました。
Mid関数もそのひとつですが、部分文字列の挿入
であるMidステートメントはありません。

S = "1234567890"
Mid(S, 3, 5) = "ABC"

そこで逆Mid関数ともいうべきMidステートメントを
シミュレートする手続きを作ってみました。

procedure MidInsert(var S1: String; P, N: Integer; S2: String);
var
  L: Integer;
begin
  L := Length(S1);
  if N > (L - P + 1) then
    N := (L - P + 1);
  if N > Length(S2) then
    N := Length(S2);
  Delete(S1, P, N);
  Insert(Copy(S2, 1, N), S1, P);
end;

{ テスト例: }
procedure TForm1.Button1Click(Sender: TObject);
var
  S: String;
begin
  S := '1234567890';
  MidInsert(S, 5, 3, 'ABC');
  ShowMessage(S);
end;

Delphiの文字列型は配列と同様の扱いも出来るのでDelete, Insert, Copyを
使わない別の方法もあります。一時オブジェクトが作成されない分、
こちらの方が高速に動作するかもしれません。

procedure MidInsert(var S1: String; P, N: Integer; S2: String);
var
  L, I: Integer;
begin
  L := Length(S1);
  if N > (L - P + 1) then
    N := (L - P + 1);
  if N > Length(S2) then
    N := Length(S2);
  StrMove(Pchar(@S1[P]), PChar(S2), N);
end;
参照: <PASCAL>

0309  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 2001/05/25 濱野 rev 1.6
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2001/06/02 K.Takaoka 編集
トークンの切り出し

Delphiのライブラリにはstrtokのようなトークンの切り出し
を行なう関数や手続きが無い為、自作することになります。

以下のコードはCopyとIsDelimiter関数を使ったトークンの
切り出し例です。


{$APPTYPE CONSOLE}
program Typing;
uses
    SysUtils;
  
procedure TypingToken(S, Delims: String);
  var
      TokenIn : Boolean; // 進行中であるかのフラグ
      TokenS, TokenLen, // トークンの開始位置と長さ
      I: Integer;
  begin
      TokenIn := False;
      TokenS := 0;
      TokenLen := 0;
      for I := 1 to Length(S) do
      begin
          if not IsDelimiter(Delims, S, I) then begin
              if TokenIn then
                  Inc(TokenLen)
              else begin
                  TokenS := I;
                  TokenLen := 1;
                  TokenIn := True
              end
          end else begin
              if TokenIn then begin
                  Writeln(Copy(S, TokenS, TokenLen));
                  TokenIn := False;
                  TokenLen := 0;
              end
          end
      end;
      if Tokenlen > 0 then
          Writeln(Copy(S, TokenS, TokenLen));
  end; { TypingToken }
const
  Delims = #9#10#13'[](){};, ';
var
  Buf: String;
begin
  while not EOF(Input) do begin
      Readln(Buf);
      TypingToken(Buf, Delims)
  end
end.


※リダイレクトしない場合、最後に[Ctrl]+[Z]を入力
して下さい。
参照: <PASCAL>

0234  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/09/04 西坂良幸 rev 1.8
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/09/16 osamu 編集
Pascal で文字列を効率良く扱う(例:文字列を逆順にする)。

MLの全体がコーディングの勉強ですが、[Delphi-ML:42129] から始まる一連のスレッド「文の逆」は、Pascal で文字列(文)を効率良く扱う様々なコーディングの例が示されています。いろいろな方法があることがわかりますね。勉強になります。

実装で気をつけるのは、2バイト文字と、CR/LF の順を入れ替えないようにしなければならないというところです。

(1) 何も考えないで作る
function StrReverse(s: string): string;
var i: Integer;
    LeadBytes: set of Char;
begin
  i:= 1;
  Result:= '';
  LeadBytes:= SysUtils.LeadBytes + [#13];
  while i<=Length(s) do begin
    if s[i] in LeadBytes then begin
        Result:= Copy(s,i,2) + Result;
        i:= i+2;
    end else begin
        Result:= Copy(s,i,1) + Result;
        i:= i+1;
    end;
  end;
end;

この関数が、ループ中で何百回も一度に呼ばれるとは考えにくいので、実はこれで十分かもしれませんが、後学のために工夫の余地を考えてみることにしましょう。

(2) [Delphi-ML:42142] 基本的な形です。(ただし、処理速度を考慮しています。)
function StrReverse(const s: string): string;
var
  i, L: integer;
begin
  L := Length(s);
  SetLength(result, L);
  i := 1;
  while i <= L do
  begin
    if (s[i] in SysUtils.LeadBytes) or
      ((s[i] = #13) and (S[i+1] = #10))
    then
    begin
      result[L-i] := s[i];
      result[L-i+1] := s[i+1];
      Inc(i, 2);
    end
    else
    begin
      result[L-i+1] := s[i];
      Inc(i);
    end;
  end;
end;

コード(1)も同じなんですが、実はこれデータによっては s[Length(s)+1] を参照してしまいます。不正な文字列の場合は無視するにしても、改行を常に #13#10 と仮定して(1)のように比較部分を単純化するか、i=Length(s) のときには s[i+1] を参照しないか、どちらかにしないと意味が無いです。

ま、それは置いといて、、、
 文字列の生成回数を減らす(Copy 関数は値を返すのに string を生成する)
 文字列の足し算の回数を減らす(足し算も内部で新しい文字列を作る)
 関数の呼び出しを最小限にする(Length の値を L にキャッシュ)
 i:=i+1 の代わりに Inc 関数を使う(C での ++ 演算子と同等)
 引数に const をつける(これも文字列の生成を押さえられる)
などが高速化部分。効果が大きい順に並べてみました。今回の目的には後ろの3つはたいした違いは生みません。特に、Length 関数のコストはそれほど大きくないことは覚えておいても良いと思います。逆に文字列の連結は時間がかかるので注意。

(3) [Delphi-ML:42157] ポインタの使い方の見本のような例ですね。
function StrReverse(const s: string): string;
var
  p, pn, pr: PChar;
begin
  SetLength(Result, Length(s));
  p := PChar(s);
  pr := PChar(Result) + Length(s);
  while p^ <> #0 do begin
    if (p^ = #13) and ((p+1)^ = #10) then
      pn := p + 2
    else
      pn := CharNext(p);
    System.Move(p^, (pr-(pn-p))^, pn-p); Dec(pr, pn-p);
    p := pn;
  end;
end;

コード(2)から、さらに [ ] の使用を無くして高速化を図ったものです。Pascal の [ ] は C の [ ] に比べてかなり時間がかかります。CharNext という関数も覚えていてよさそうですね。でも高々2バイトの転送に System.Move を使うよりは、(2) のように SysUtils.LeadBytes を使って判定して1バイトずつ ^ で転送した方がずっと速いでしょう。

2バイト文字の判断で2バイト目が #0 でないことを確認しないと、不正なデータが与えられた時に、メモリを破壊してシステムをハングアップさせる危険性があります。p_end:=PStr(s)+Length(s); としておいて、while の比較を、p<=p_end とすればハングアップを防ぎつつさらに高速化にもなりますね。

(4) [Delphi-ML:42148] ポイントは再帰ですね。
function StrReverse(const s: string): string;
var
  L: integer;
begin
  result := '';
  L := Length(s);
  if L = 0 then
    exit;
  if IsDBCSLeadByte(Byte(s[1])) then
    result := StrReverse(copy(s, 3, L-2)) + s[1] + s[2]
  else
    result := StrReverse(copy(s, 2, L-1)) + s[1];
end;

コードはすっきりしますが、速度的には(1)よりずっと悪いです。なにより、長〜い文字列を渡すと再帰が深くなりすぎて、スタックオーバーフローを起こしかねないので、この目的には使えません。えっと、この例だけ改行文字を考慮していないですね。

(5) [Delphi-ML:42155] WideStringを使う発想です。
function StrReverse(s: WideString): WideString;
const
  WCharCR : WideChar = #$0d;
  WCharLF : WideChar = #$0a;
var
  i  : Integer;
begin
  SetLength(Result, Length(s));
  for i:=1 to Length(s) do begin
    if (s[i] = WCharCR) and (s[i+1] = WCharLF) then begin
      s[i] := WCharLF; s[i+1] := WCharCR;
    end;
    Result[Length(s)-i+1] := s[i];
  end;
end;

WideString を使うことで、2バイト文字を考えなくても良くなります。もし、改行文字が含まれないことが分かっていれば、if 文は必要無くなりますから、非常にすっきりしたコードになります。WideString <-> string の変換はキャストの必要無く自動で行われます。変換は時間がかかりますが、初めと終わりの2回だけなので、この場合には気にする必要は無いでしょう。美しいコードです。

(6) [Delphi-ML:42173] 文字列の両端から交換していこうというアイディア
function StrReverse(const s: string): string;
var
  len, i : integer;
  L, R : pchar;
  tmp : char;
  p : pchar;
begin
  Result := s;
  len := length(s); if len = 0 then exit ;
  p := PChar(Result);
  L := p; R := L + len - 1;
  while L < R do
  begin
    if (L^ in Sysutils.LeadBytes) or
      ((L^ = #13) and ((L+1)^ = #10)) then
    begin
      tmp := L^; L^ := (L+1)^; (L+1)^ := tmp;
      inc(L, 2);
    end
    else
      inc(L);
  end;
  L := p; R := L + len - 1;
  for i := 1 to (len div 2) do
  begin
    tmp := L^; L^ := R^; R^ := tmp;
    inc(L); Dec(R);
  end;
end;

うーん何やら複雑。改行文字や2バイト文字が無くて、なおかつ関数が
    procedure StrReverse(var s: string): string;

のように、直接引数を書きかえるものなら、メリットはあるかもしれません。。。この場合には、ほぼ最後の for 文だけになってしまいます。

基本的に、ループの中で何度も繰り返し呼ばれるので無ければ、実行速度を求めて読みにくいコードを書くのは愚とされます。したがって、この問題に限って言えば、(1) や (5) のように分かりやすく書けるものがお勧めです。
ただし、実行効率を考慮しないといっても限度があり、(4) のように、長い文字列を渡すとスタックオーバーフローを起こすようなコードは、いかに見やすくても不適当でしょう。

あなたならどう書きますか? アイデアある方はどんどん加筆して下さい。
参照: [Delphi-ML:42134] <PASCAL>

0261  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/09/15 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/09/15 osamu 編集
漢数字で位取り表示

数字を漢数字で位取り表示(4桁単位)する関数を作ってみました。

function RhKnjNumber(const Num: Currency): string;
begin
  Result := FormatFloat('####"兆"####"億"####"万"####円', Num);
  while True do {余計な文字を取り去る}
    if ByteType(Result, 1) = mbLeadByte then
      Result := Copy(Result, 3, Length(Result) - 2)
    else
      Break;
end;
参照: [Delphi-ML:34500] <PASCAL>

0250  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/09/09 osamu rev 1.2
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/09/09 osamu 編集
文字列を TDateTime に変換する

文字通り StrToDateTime という関数もあるんですが、これは yyyy/mm/dd などの形式にしか対応していません。ちょっとひねって VarToDateTime を使えば、ほぼどんな形式でもうまく変換できます。string -> Variant は自動で変換されるので、そのまま文字列を渡すだけで使えます。

MyDateTime := VarToDateTime('平成1年1月1日午後1時1分1秒');

全角数字もOKですが、さすがに '平成九年九月九日' は無理でした。

VarToDateTime('1999/1/2') = VarToDateTime('1/2/1999');

の結果は、True になりました。

注意点として、VerToDateTime は StrToDateTime と違い、ShortDateFormat グローバル変数を見ていないようです。

    ShortDateFormat:='DD/MM/YY';
    ShowMessage(FloatToStr(VarToDateTime('2/1/1999')-VarToDateTime('平成11年2月1日')));

の結果は 0 になります。
参照: <日時> <PASCAL>

0203  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/08/13 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/08/13 osamu 編集
TColor 値を文字列に変換する

レジストリに TColor 型の値を格納したいような場合、ColorToString 関数を使えば TColor から AnsiString に変換できるので、あとは TRegistry の WriteString で書き出してやればいいと思います。
反対に AnsiString から TColor に戻すのは StringToColor 関数を使えばいいです。
詳しくはヘルプを見てください。
参照: [builder:17839] <PASCAL> <描画>

0186  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/05/14 おばQ rev 1.2
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/05/14 おばQ 編集
文字数のカウント方法

普通にLength関数で文字数をカウントすると半角文字の数になります。例えばLength('あいうえおABCDE')は15です。

全角文字を1文字としてカウントするにはWideStringを使います。Length(WideString('あいうえおABCDE'))は10です。

WideStringにキャストするより高速な手法としてByteToCharLenを使う方法もあります。
ByteToCharLen('あいうえおABCDE', Length('あいうえおABCDE'))は10です。
参照: <PASCAL>

0181  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/05/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/05/08 osamu 編集
改行コードの違いについて

テキストを扱っていると、何かと改行コードについて気になります。

改行コードは Windows では CRLF、Macでは CR のみ、Unix 系では LF のみで示されます。制御コードとして CR が復帰 LF が改行と呼ばれます。Delphiでは CR=Char(13) または #13、LF=Char(10) または #10 として指定できます。

Label 等ではテキストの改行を行う場合、CRLF, CRのみ, LFのみ、どの場合でも改行が行えますので
Label.caption := '1行目'#10'2行目'という表現も可能です。

また、改行コードが、CR のみ、LF のみ、LFCR になっているテキストを CRLF シーケンス(つまりWindows標準形式)に変換する関数としてAdjustLineBreaks というものがあります。逆に CRLF から、CR のみLF のみ、に変換するには

  function DeleteCR(S: string): string;
  begin
    while pos(#13,S)<>0 do
      Delete(S,pos(#13,S),1);
    Result := S;
  end;

このようなものを使ってください。DeleteLFも作れるでしょう
(D4以降ではStringReplace関数が用意されていますのでそちらを使いましょう)
参照: [Delphi-ML:9035] <PASCAL>

0151  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/11 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/11 osamu 編集
和暦を西暦に直したい

こんなのはどうでしょう?

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key<>#13 then
    Exit;
  Key:= #0;
  with Edit1 do
  try
    Text:= FormatDateTime('yyyy/m/d',VarToDateTime(Text));
    SelectAll;
  except
    on EVariantError do begin
      SelectAll;
      raise Exception.Create('入力が不正です');
    end;
  end;
end;

98/8/21 h10/8/21 s24-10-2 m1/1/1 8/21 8/21/98 98/5 1996-8/21
等 ほとんど何でもありです。半角、全角関係なしです。
参照: [Delphi-ML:25313] [Delphi-ML:25315] <計算> <日時> <PASCAL>

0033  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
Delphi3 の TStringList.CommaText の不具合

個々のアイテムが空白などを含む場合にのみダブルクオートで括るはずが、必要の無いときまでダブルクオートが使われる。

[対応策]

class.pas の 2253 行目から4行だけ示します。"" で包む必要があるなら
包む処理をする部分です。

      S := Get(I);
      P := PChar(S);
      while not (P^ in [#0..' ','"',',']) do P := CharNext(P);
      if (P <> #0) then S := AnsiQuotedStr(S, '"');
      //  ^^  ここ正しくは P^ 。
      // ポインタと文字を比べるんじゃなーい!!
参照: [Delphi-ML:8468] <バグ> <PASCAL>

0022  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
文字列の切り分け

 >> 以下のような文字列dumStringにおいて、その中に含まれている引数term3だけを得たい。
 >>
 >> dumString := 'program term1 term2 term3 term4 term5';

GetTokenIndex(dumString,' ',4)でterm3を返します。

{---------------------------------------------------------------------}
function RemoveToken(var s:string;delimiter:string):string;
var p:Integer;
begin
    p:=Pos(delimiter,s);
    if p=0 then Result:=s
           else Result:=Copy(s,1,p-1);
    s:=Copy(s,Length(Result)+Length(delimiter)+1,Length(s));
end;
{---------------------------------------------------------------------}
function GetTokenIndex(s:string;delimiter:string;index:Integer):string;
var i:Integer;
begin
    Result:='';
    for i:=0 to index do
        Result:=RemoveToken(s,delimiter);
end;
{---------------------------------------------------------------------}

RemoveTokenはsをdelimiterで切り分けたときの一番始めの部分を返し、sから取り除きます。sにdelimiterが含まれないときにはs全体を返し、sは空文字列になります。

GetTokenIndexは、sをdelimiterで切り分けたときのindex番目の部分を返します。一番始めの部分はindex=0です。sは変更されません。
indexが範囲外の場合には空文字列が返ります。

delimiterはstring型になっているので、#13#10や' : 'などで切り分けるのもOKです。
参照: [Delphi-ML:12110] <PASCAL>

[新規作成] [最新の情報に更新]

How To
Lounge
KeyWords

Tips
Delphi
Home
Osamu Takeuchi osamu@big.or.jp