†Sibylのお部屋†

【Sibyl】文字列操作と書式指定

作成開始日 2009.10.15
最終更新日 2023.02.04

基本的な文字列操作関数

Sibylの文字列操作機能は、REXXなどと比較してかなり貧弱。Left関数やRight関数に直接相当する機能すらない。しかも、関数とプロシージャが混在していて、使用する際にもかなり混乱する。状況はDelphiでも同様で、DelphiにLeftStr関数やRightStr関数が追加されたのはver.6かららしい。

機 能関 数プロシージャ
文字列の切り出しst:=Copy(st,n,m)SubStr(st,n,m)
文字列の検索n:=Pos(sub,st)--
文字列の長さn:=Length(st)--
文字列の削除--Delete(st,n,m)
文字列の挿入--Insert(sub,st,n)
文字列の置換(下記参照)
Left関数相当st:=Copy(st,1,n)
Right関数相当st:=Copy(st,Length(st)-n+1,n)
文字列の比較 n:=CompareStr(st1, st2) 大文字・小文字区別
n:=CompareText(st1, st2) 大文字・小文字同一視
n:=AnsiCompareText(st1, st2) 〃 こちらの方が並びが自然
数値の文字列化st:=toStr(x)Str(x,st)
文字列の数値化--Val(st,n,code) 末尾はエラーコード
数値の
16進文字列化
st:=toHex(n)
st:=IntToHex(n,d) dは桁数
--

けっこうアタマに来ているのは、Copy/Delete/Insertで、そもそもこんな汎用的で目的が特定できない名称ではヘルプ検索すら一苦労なのだが、Copyが関数で、InsertとDeleteがプロシージャって、どういうこと?全部関数でいいじゃん!紛らわしくてしょうがない。しかも、Copyのプロシージャ版であるSubStrまであるって、どうなってんの?おまけに、DeleteとInsertの引数の順番が不統一だし…[元文字列、開始点、操作対象(文字数/文字列)]に統一しろよ!

文字コード関数

Chr(n)		文字コードnの文字	c:=Chr(9)	//cはタブ文字
Ord(c)		文字cの文字コード	n:=Ord('A')	//nは65になる
※Ordは文字コードだけでなく、任意の列挙型変数に対して使用できる
※Sibylのhelpには「Write(Chr(#9));」というサンプルコードがあるが「Chr(9)」の誤り
#n		文字コードnの文字	c:=#9		//タブ
#$hh		16進文字コードhhの文字	c:=#$27		//シングルクォート

文字列の置換

Sibylには文字列置換関数がない。Delphaiでは後年StringReplace関数が追加されたようだが、SibylではPos/Insert/Deleteを組み合わせるしかないようだ。しかも、Left/Rightとは違って簡単に1行で済ますことはできない。また、下記の例では最初の検索対象しか置換できない。文字列に複数の置換対象がある場合には再帰ループ化する必要がある。
//文字列「st」の中から検索文字列「stFind」を探し出し、置換文字列「stRepl」に置換する

p:=pos(stFind,st);      //検索文字列stFindの位置
w:=length(stFind);      //検索文字列stFindの長さ
Delete(st,p,w);         //検索文字列stFindの削除
Insert(stRepl,st,p);    //置換文字列stReplの挿入

//敢えて2行で書けば;ほとんどワケわからんが…

Delete(st,pos(stFind,st),length(stFind));  //検索文字列stFindの削除
Insert(stRepl,st,pos(stFind,st));	   //置換文字列stReplの挿入
なお、こうした汎用的な置換ではなく、特定の1文字を別の1文字に置換するだけなら、文字列の配列要素を直接いじる方が早い。たとえば、「yy/mm/dd」形式の文字列を「yy-mm-dd」形式に置換する場合、Pos/Insert/Deleteよりも、3番目と6番目の配列要素に「-」を代入する方が簡単。

書式と整形

Sibylには、数値や文字列を「整形した文字列」に変換する

Format関数とFloatToStrF関数

が用意されている。その他にも、類似の関数はいくつかあるが、この二つが代表。まあ、Writeln文の書式指定子を独立させたような機能なのだが、それよりも機能はかなり豊富になっている。ただし、使い方が相当面倒な上に、私が調べた限りでは、これまたバグの巣窟なのである(;_;)

まず、基本となるFormat関数の書式だが、Sibylのヘルプで理解しろと言うのは無理なので…

      形式:Format('書式指定', [変数]);
      構文:Format('%《番号》:《桁数》《形式》…',[《変数1》,《変数2》,…]);
      用例:st:=Format('%0:5s  %1:5d  %2:-8s  %3:10.3f',[st1, n, st2, r]);
Format関数の最初の引数である文字列は、書式の指定子。書式の指定子の形式は、「%」のあとに《番号》を付けて、何番目の変数に相当するかを指定し、「:」以下で桁数と形式を指定する。書式指定子に識別番号が付いているということは、変数が必ずしも表示される順番に並んでなくてもよいということ。また、書式指定子以外の文字は、そのまま表示される。

書式の《桁数》は文字通り表示桁数で、5を指定すれば5桁分スペースが確保され、そこに変数の中身が表示される。浮動小数点の場合は、「.」を付けて小数点以下の桁数も指定できる。また、表示は原則左寄せ(頭揃い)だが、桁数の前に「-」を付けると右寄せ(尻揃い)になる。また、文字列の場合は逆にデフォルトで右寄せ、「-」を付けると左寄せになる。

文字列・数値の右寄せ/左寄せはFormat関数が便利

表示の《形式》は、sが文字列、dが整数、fが浮動小数点、eが指数表現、mが通貨形式など。詳しくはヘルプ参照。なお、変数の型は厳密に一致していなくてはならない。整数と実数の間にも互換性はない。

Format関数の二番目の引数は、表示する変数だが、必ず

[ ]で括った列挙型として扱う。

個別の変数を単純に並べただけでは駄目である。最初は絶対に間違えるので注意!

3桁区切りの問題

数値を3桁区切りのカンマ(,)付きで表示するときも、書式指定関数を使うと便利。3桁区切りの数値は、通貨形式とN形式でサポートしている…ハズである。「ハズ」というのは、実際にはきちんとサポートされていないから。まず、N形式はヘルプに「3桁区切りを含む」(contain thousand separators)と明記されているにも拘らず、実際には表示されない。通貨形式の場合は確かに3桁区切りが入るが、小数点以下の表示を0にすると3桁区切りも消えてしまう。また、先頭に通貨記号が付く。ということで、ファイルサイズなどを3桁区切り付きで表示する際には非常に困ってしまうのであった。

で、そんなときには、

FloatToStrF関数を使う。

これは、基本的にはFormat関数と同じようなものだが、実数の文字列変換に特化した関数で、以下のような特徴がある。 このFloatToStrF関数を使って、整数nを12桁(三桁区切り含む)の右寄せ文字列に変換するには、
    st:=FloatToStrF(n,ffNumber,12,1);
    st:=Copy(Format('%0:14s',[st]),1,12);
最初のFloatToStrFで三桁区切り付きにしているが、左寄せ文字列だし、小数点以下を付けざるをえない。そこで、次のFormatで右寄せにして、Copyで小数点以下を切り捨てている。

3桁区切りの関数の自作 (2022.04.13追記)

むしろ、以下のような関数を作っておく方が現実的かと。
//-----------------------------------------------------------------------------
  Function TForm1.SANKETA(n:LongInt):String;
//-----------------------------------------------------------------------------
Var
  st : String;
  i  : Integer;
  len, mm: Integer;
Begin
  st:=toStr(n);		//数値を文字列化
  len:=Ord(st[0]);	//文字列の長さ、Length使う方がスマートか?

  mm:=len div 3;			//3桁区切りの数
  if (len mod 3)=0 then mm:=mm-1;	//桁が3の倍数の場合の補正

  For i:=1 to mm Do
  Begin
    Insert(',',st,len-i*3+1);	//カンマを挿入(後ろから)
  End;

  Result:=st;
End;

容量のKB/MB/GB表示 (2022.04.13追記)

ファイルやディスクの容量は、KB/MB/GB単位で表示する方が便利なことも多い。と言うことで、以下のような関数を作成した。ここで留意すべき点は、引数がLongInt型であるため、扱える上限が約2GBである点。DiskFree関数やDiskSize関数の戻値もLongInt型であるし、TSeartch.SizeもLongInt型;つまり、Sibylでは2GB以上の容量を想定していないようだ。なお、DiskFree関数の戻値をLongWord型で受ければ約4GBまでは扱えるようになるが、これも根本解決には程遠い。

また、以下のアルゴリズムでは、サイズの数値は各単位で切り捨てられる。たとえば、2,900,000 byteのファイルは「2MB」となる。小数点以下の数値も表示したい場合は、引数を実数化して、Format関数で表示形式を整えることになる。整数のままゴニョゴニョする方法もないではないが…

//-----------------------------------------------------------------------------
  Function TForm1.KMGunit(n:LongInt):String;    //kB,MB,GB表示
//-----------------------------------------------------------------------------
Var
  u: String;
Begin
  u:='';
  if n>1024 then begin n:=n div 1024; u:='kB' end;
  if n>1024 then begin n:=n div 1024; u:='MB' end;
  if n>1024 then begin n:=n div 1024; u:='GB' end;

  Result:=toStr(n)+u;
End;

日付形式−先行0の問題

yy-mm-dd」形式の日付表示などの場合、数値に先行する0を付けたいことが多い。「09-10-05」が「9-10-5」ではカッコ悪いのだ。Format関数を使えば、桁を揃えて「 9-10- 5」とすることは簡単だが、先行0は付かない。しょうがないので、あとは工夫で勝負。たとえば、日付の構造体dt(DateTime型)に日付データが入っている場合、
    st:=toStr(dt.year*10000+dt.month*100+dt.day);
    st:=st[3]+st[4]+'-'+st[5]+st[6]+'-'+st[7]+st[8];
てな感じかな。年、月、日を一つのLongInt型の整数にしちゃってから文字列化するのがミソ。時間の方も似たような処理ができる。
    st:=toStr(10000+dt.hour*100+dt.min);
    st:=st[2]+st[3]+':'+st[4]+st[5];
てな感じで。

型指定と型変換

数値を整形表示する際には型変換は重要。もちろん、数値→文字列の変換も重要だが、Format関数のように整数/実数の互換性がない関数では、整数の実数化などの型変換も重要になる。先にこちらの問題に触れておくと、

整数の実数化にはInt関数を使う

のである。そう、実数化はInt関数!RealでもFloatでもなく、Int関数! もちろん、本来は実数の整数部分のみを取り出す関数で、整数の実数化のため関数ではない。結果的に整数の実数化にも使えると言うことに過ぎない。う〜む、理屈はわかるが…

標準Pascalでは、数値を文字列に変換するにはStr手続きを使った。関数ではないのである。これは猛烈に使いにくい。ということで、後にStr手続きを関数化したtoStr関数が追加された。


【Sibylのお部屋目次】 【ホーム】