Tips その1


文字列が浮動小数点値かどうか確かめる

こんな関数を使ってみてください。もうそのまんまです(笑)

チェックしたい文字列を引数に渡して、それが浮動小数点値だったらTrue、そうでなければFalseを返す関数です。

function CheckFloat(Value: String): Boolean;
var
  i: Integer;
  float_mark: Boolean;
  float_dot : Boolean;
begin
  Result:= False;
  if Value = '' then Exit; // 文字列が空なら終了
  float_mark:= False;
  float_dot:= False;
  for i:= 1 to Length(Value) do begin
    if i = 1 then begin //1文字目をチェック
      if not (Value[i] = '+')
      and not (Value[i] = '-')
      and not (Value[i] = '1')
      and not (Value[i] = '2')
      and not (Value[i] = '3')
      and not (Value[i] = '4')
      and not (Value[i] = '5')
      and not (Value[i] = '6')
      and not (Value[i] = '7')
      and not (Value[i] = '8')
      and not (Value[i] = '9')
      and not (Value[i] = '0')
      and not (Value[i] = '$')
      and not (Value[i] = '#') then begin
        Exit;
      end;
    end else begin
//2文字目以降をチェック
      if Value[i] = '.' then begin
        if float_dot then begin
          Exit;
        end else begin
          float_dot:= True;
          Continue;
        end;
      end;
      if Value[i] = 'e' then begin
        if float_mark then begin
          Exit;
        end else begin
          float_mark:= True;
          Continue;
        end;
      end;
      if not (Value[i] = '1')
      and not (Value[i] = '2')
      and not (Value[i] = '3')
      and not (Value[i] = '4')
      and not (Value[i] = '5')
      and not (Value[i] = '6')
      and not (Value[i] = '7')
      and not (Value[i] = '8')
      and not (Value[i] = '9')
      and not (Value[i] = '0') then begin
        Exit;
      end;
    end;
  end;
  Result:= True;
end;

上の場合、判断する要素が文字型で256個以内なので、次のようにするともっと効率的です。

function CheckFloat(Value: String): Boolean;
var
  i: Integer;
  float_mark: Boolean;
  float_dot : Boolean;
begin
  Result:= False;
  if Value = '' then Exit; // 文字列が空なら終了
  float_mark:= False;
  float_dot:= False;
  for i:= 1 to Length(Value) do begin
    if i = 1 then begin //1文字目をチェック
      if not (Value[i] in ['+', '-', '0'..'9', '$', '#']) then Exit;
    end else begin
//2文字目以降をチェック
      if Value[i] = '.' then begin
        if float_dot then begin
          Exit;
        end else begin
          float_dot:= True;
          Continue;
        end;
      end;
      if CompareText(Value[i], 'e') = 0 then begin
        if float_mark then begin
          Exit;
        end else begin
          float_mark:= True;
          Continue;
        end;
      end;
      if not (Value[i] in ['0'..'9']) then Exit;
    end;
  end;
  Result:= True;
end;

あと、FloatToStr()try... except... で囲ってチェックするのも手かと思います。
(実際、そっちの方が速いか?)

もっと効率のいい方法があったら教えてください。


文字列が整数値かどうか確かめる

浮動小数点のときの応用です。こちらはもっと簡単。

function CheckInteger(Value: String): Boolean;
var
  i: Integer;
begin
  Result:= False;
  if Value = '' then Exit;
  for i:= 1 to Length(Value) do begin
    if i = 1 then begin
      if not (Value[i] in ['+', '-', '0'..'9']) then Exit;
    end else begin
      if not (Value[i] in ['0'..'9']) then Exit;
    end;
  end;
  Result:= True;
end;

これも、IntToStr()try... except... で囲ってチェックする手もあるし、IntToStrDef() でデフォルト値をチェックするという方法もあるでしょう。


文字列でゼロサプレスするには

こんなの、どう?

function ZeroSupress(Value: String): String;
var
  sBuf: String;
  L : Integer;
  i : Integer;
begin
  { 文字列長取得 }
  L:= Length(Str);
  { "0"を削る }
  sBuf:= '';
  for i:= 1 to L do
    if (Str[i] <> '0') and (Str[i] <> #0) then sBuf:= sBuf + Str[i];
  Result:= sBuf;
end;

Str[i] <> '0' のところの '0' を他の条件にすることで、ゼロ以外の文字列も削れますな。


デリミタを判断して文字列を分解するには

もしかすると、VCLに標準でこういうのがあるかもしれませんが。

この例は、Derimiter で各フィールドが区切られた文字列 Src を分析して、各フィールドの文字列を Values に格納しています。Values はあらかじめインスタンスを作っておく必要があります。ここではフィールド数を復帰するようになってますが、これは、Values.Count を見ることでも分かります。

function ReadDelimiterText(const Src: String;
                            Delimiter: Char;
                            var Values: TStringList): Integer;
var
  i : Integer;
  src_len : Integer;
  val_cnt : Integer;
  str_buf : String;
begin
  { 復帰を0で初期化 }
  Result:= 0;
  { 文字列チェック }
  if Src = '' then Exit;
  { デリミタチェック }
  if Delimiter = '' then Exit;
  { 読込行数リセット }
  val_cnt:= 0;
  { 文字列長取得 }
  src_len:= Length(Src);
  { バッファリセット }
  str_buf:= '';
  { 区切り解析 }
  for i:= 1 to src_len do begin
    if Src[i] = Delimiter then begin
      Values.Add(str_buf);
      str_buf:= '';
      Inc(val_cnt);
    end else begin
      str_buf:= str_buf + Src[i];
    end;
  end;
  { 最後の文字列を追加 }
  Values.Add(str_buf);
  { 読込値数を返す }
  Result:= val_cnt + 1;
end;

Delphi6なら TStringList の新しいプロパティ DelimitedText を使って次のようにもできます。

function ReadDelimiterText(const Src: String;
                            Delimiter: Char;
                            var Values: TStringList): Integer;
begin
  { 復帰を0で初期化 }
  Result:= 0;
  { 文字列チェック }
  if Src = '' then Exit;
  { デリミタチェック }
  if Delimiter = '' then Exit;
  { デリミタを設定 }
  Values.Delimiter:= Delimiter;
  { 区切り文字列に引数の文字列をセット }
  Values.DelimitedText:= Src;
  { 行数を返す }
  Result:= Values.Count;
end;

Delphi5以前のバージョンにも CommaText というのがあって、これはデリミタがコンマ","に限られますが、同様のことができると思います。

TStringList の代わりに TStringField を使うと、さらに便利になるかも?


[ Delphi へ戻る ]