//////////////////////////////////////////////////////////// //数式文字列を計算する処理 //00/07/24 //////////////////////////////////////////////////////////// unit StringCalculation; interface uses Windows, Messages, SysUtils, Classes, Math; function StrCaluculation(Suushiki: String): Real; implementation type TMojiShurui = (msSuuchi, msEnzanshi, msKakko); TOneMoji = String[1]; //////////////////////////////////////////////////////////// //汎用的な文字列処理 //------------------------------- //Sの中にSubStrがいくつあるかカウントする {適当な処理だぞ、} function StrCount(const Substr, S: string): Integer; var Str: String; begin Result := 0; if (Substr = '') or (S = '') then begin Result := -1; exit; end; Str := S; try while AnsiPos( Substr, Str) <> 0 do begin Inc(Result); delete(Str, AnsiPos( Substr, Str), Length(Substr)); end; except Result := -1; end; end; {AAAからAAをカウントすると一つになる} //------------------------------- //テキスト後方検索 function BackPos(subStr,S: String): Integer; var SerchStr: String; BackPosIndex: Integer; begin if Pos(subStr,S)=0 then begin Result := 0; Exit; end; SerchStr := S; while pos(subStr,SerchStr)<>0 do begin BackPosIndex := pos(subStr,SerchStr); Delete(SerchStr,1,BackPosIndex); end; Result := length(S)-Length(SerchStr); end; {2バイト文字には非対応} //------------------------------- //先頭からNumber個目のSubstrの位置を調べる function PosLoop(Substr: string; S: string; const Number: Integer): Integer; var i: Integer; PosIndex: Integer; begin Result := 0; if Number < 1 then begin raise Exception.Create('ばぐ'); exit; end; for i := 1 to Number do begin PosIndex := Pos(Substr, S); if PosIndex <> 0 then begin delete(S, 1, PosIndex); Inc(Result, PosIndex); end else begin Result := 0; exit; end; end; end; {2バイト文字非対応} //////////////////////////////////////////////////////////// //数式演算関数 const AlphabetTable: String = 'abcdefghijklmnopqrstuvwxyz'+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; {↑26x2個の数字までしか動作出来ない  (文字を増やせばいくらでも出来るけど52で十分だろ)} //------------------------------- //文字列が『().0_9+-*/^』で構成されていればTrueそれ以外はfalse function IsthisSuushiki(Suushiki: String): Boolean; var i: Integer; begin Result := true; for i := 1 to Length(Suushiki) do begin if Pos(Suushiki[i], ' ().0123456789+-*/^') = 0 then begin Result := false; break; end; //if end; //for end; //------------------------------- //数式を単なるカンマ区切りの数値の羅列にする //マイナスもちゃんと含めるようにしよう。 function SuuchiKiridashi(Suushiki: String): String; begin {↓負の値の変換} if Suushiki[1] = '-' then Suushiki[1] := '_'; Suushiki := StringReplace(Suushiki, '+-', '+_', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '--', '-_', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '*-', '*_', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '/-', '/_', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '^-', '^_', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '(-', '(_', [rfReplaceAll]); {↓数値以外の値をスペースに置き換える} Suushiki := StringReplace(Suushiki, '(', ' ', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, ')', ' ', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '+', ' ', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '-', ' ', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '*', ' ', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '/', ' ', [rfReplaceAll]); Suushiki := StringReplace(Suushiki, '^', ' ', [rfReplaceAll]); {↓連続スペースを一つのスペースにする} while Pos(' ', Suushiki) <> 0 do Suushiki := StringReplace(Suushiki, ' ', ' ', [rfReplaceAll]); {途中で利用した'_'を'-'に変換する} Suushiki := StringReplace(Suushiki, '_', '-', [rfReplaceAll]); Result := Suushiki; end; //------------------------------- { 数式から括弧の対応位置を知らせてくれる関数 戻り値が0なら、指定場所が括弧文字ではないってこと } function TaiouKakkoHaDokoyanenn(const Suushiki: String; const kakkoIndex: Integer): integer; var i, KakkoCount: Integer; begin result := 0; if not (1<= kakkoIndex) and (kakkoIndex <=Length(Suushiki)) then begin raise Exception.Create('括弧判定場所の間違いっす'); Result := 0; exit; end; {↓『(』なら対応する『)』を発見するようにする} if Suushiki[kakkoIndex] = '(' then begin KakkoCount := 0; for i := kakkoIndex to Length(Suushiki) do begin if Suushiki[i] = '(' then Inc(KakkoCount); if Suushiki[i] = ')' then begin Dec(KakkoCount); if KakkoCount = 0 then begin Result := i; exit; end; end; //if Suushiki end; //for raise Exception.Create(''); end else {↓『)』なら対応する『(』を見つける} if Suushiki[kakkoIndex] = ')' then begin KakkoCount := 0; for i := kakkoIndex downto 1 do begin if Suushiki[i] = ')' then Inc(KakkoCount); if Suushiki[i] = '(' then begin Dec(KakkoCount); if KakkoCount = 0 then begin Result := i; exit; end; end; //if Suushiki end; //for end else Result := 0; end; //------------------------------- //指定演算子を括弧で囲む {a+b*c→a+(b*c)という式にする Enzanshiには'+-*/'どれか一つの文字以外入れない事。 Suushikiはこのばあいアルファベット数式に変換されている事。} function EnzanshiKakko(Suushiki: String; Enzanshi: TOneMoji): String; {アルファベット数式から文字の種類を判定する} function KonomojiNanda(S: TOneMoji): TMojiShurui; begin if pos(S, AlphabetTable)<>0 then Result := msSuuchi else if pos(S, '+-*/^')<>0 then Result := msEnzanshi else if pos(S, '()')<>0 then Result := msKakko else raise Exception.Create('数式とちゃうがな、この文字'); end; var EnzanshiCount, EnzanshiIndex: Integer; i: Integer; begin EnzanshiCount := StrCount(Enzanshi, Suushiki); for i := 1 to EnzanshiCount do begin EnzanshiIndex := PosLoop(Enzanshi, Suushiki, i); {↓演算子の右側の『)』を追加} case KonomojiNanda(Suushiki[EnzanshiIndex+1]) of msSuuchi: begin Insert( ')', Suushiki, EnzanshiIndex+2); end; msEnzanshi: begin raise Exception.Create('演算子が重複しているぞ?'); end; msKakko: begin Insert(')', Suushiki, TaiouKakkoHaDokoyanenn(Suushiki, EnzanshiIndex+1)+1); end; end; //case {↓*の左側で『(』を追加} case KonomojiNanda(Suushiki[EnzanshiIndex-1]) of msSuuchi: begin Insert('(', Suushiki, EnzanshiIndex-1); end; msEnzanshi: begin raise Exception.Create('演算子が重複していない?'); end; msKakko: begin Insert('(', Suushiki, TaiouKakkoHaDokoyanenn(Suushiki, EnzanshiIndex-1)); end; end; //case end; //for Result := Suushiki; end; //------------------------------- //一つの括弧でくくられている式を計算して結果を数字文字で返す {(|126*|789)→|1111 (|123)→|123} function OneKakkkoEnzan(Suushiki: String): String; var LeftVal, RightVal: String; LeftValHaniHidari, LeftValHaniMigi: Integer; RightValHaniHidari, RightValHaniMigi: Integer; LeftReal, RightReal: Real; begin Result := Suushiki; if Result[1] = '(' then Delete(Result, 1, 1) else raise Exception.Create('エラエラえらー'); if Result[Length(Result)] = ')' then Delete(Result, Length(Result), 1) else raise Exception.Create('エラエラえらー'); if (pos('+|', Result)=0) and (pos('-|', Result)=0) and (pos('*|', Result)=0) and (pos('/|', Result)=0) and (pos('^|', Result)=0) then begin {演算子が入っていないのなら |123とか|-456と推定されるのでそのままResultする} Result := Result; exit; end else begin {演算子が入っているのなら} LeftValHaniHidari := 1; LeftValHaniMigi := BackPos('|', Result) - 2; LeftVal := copy(Result, LeftValHaniHidari, LeftValHaniMigi-LeftValHaniHidari+1); RightValHaniHidari := BackPos('|', Result) +1; RightValHaniMigi := Length(Result); RightVal := copy(Result, RightValHaniHidari, RightValHaniMigi-RightValHaniHidari+1); LeftReal := StrToFloat(StringReplace( LeftVal, '|', '', [])); RightReal:= StrToFloat(StringReplace(RightVal, '|', '', [])); if (Pos('+|', Result)<>0)then Result := '|'+ FloatToStr( LeftReal + RightReal) else if (Pos('-|', Result)<>0)then Result := '|'+ FloatToStr( LeftReal - RightReal) else if (Pos('*|', Result)<>0)then Result := '|'+ FloatToStr( LeftReal * RightReal) else if (Pos('/|', Result)<>0)then Result := '|'+ FloatToStr( LeftReal / RightReal) else if (Pos('^|', Result)<>0)then begin Result := '|'+ FloatToStr( IntPower( LeftReal, round(RightReal)) ); end else raise Exception.Create('エラー'); end; end; //------------------------------- //関数本体 function StrCaluculation(Suushiki: String): Real; var StringList1: TStringList; SuuchiArray: array of Real; i: Integer; AlphaSuushiki, KeisanMae, KeisanGo: String; MigiKakkoIndex, HidariKakkoIndex: Integer; begin if not IsthisSuushiki(Suushiki) then raise Exception.Create('数式ではありません'); StringList1 := TStringList.Create; try StringList1.CommaText := (SuuchiKiridashi(Suushiki)); SetLength(SuuchiArray, StringList1.Count); for i := 0 to StringList1.Count - 1 do try SuuchiArray[i] := StrToFloat(StringList1[i]); except raise Exception.Create('数値に変換できません'); end; if Length(SuuchiArray) > Length(AlphabetTable) then raise Exception.Create('数式中の数値の数が多すぎます'); AlphaSuushiki := Suushiki; for i := Low(SuuchiArray) to High(SuuchiArray) do begin AlphaSuushiki := StringReplace(AlphaSuushiki, StringList1[i], AlphabetTable[i+1], []); end; {↑AlphaSuuchikiは(a/b+c)+d*e-fのようになる  マイナス値は考慮する必要のない純粋な四則計算式になっています} finally StringList1.Free; end; {↑この時点でSuuchiArrayに数式の数値がすべて入っている} AlphaSuushiki := EnzanshiKakko(AlphaSuushiki, '^'); AlphaSuushiki := EnzanshiKakko(AlphaSuushiki, '*'); AlphaSuushiki := EnzanshiKakko(AlphaSuushiki, '/'); AlphaSuushiki := EnzanshiKakko(AlphaSuushiki, '+'); AlphaSuushiki := EnzanshiKakko(AlphaSuushiki, '-'); {↑アルファベット数式の演算子に対応する括弧を付ける} Suushiki := AlphaSuushiki; for i := Low(SuuchiArray) to High(SuuchiArray) do begin Suushiki := StringReplace(Suushiki, AlphabetTable[i+1], '|'+FloatToStr(SuuchiArray[i]), []); end; {↑数式は((a+b)-c)>>((|123+|456)-|789)と変換される} while pos('(', Suushiki) <> 0 do begin {↓始めの『)』の場所を見つけて対応する『(』を見つける} MigiKakkoIndex := pos(')', Suushiki); HidariKakkoIndex := TaiouKakkoHaDokoyanenn(Suushiki, MigiKakkoIndex); {一つの『()』の組み合わせを計算する} KeisanMae := copy(Suushiki, HidariKakkoIndex, MigiKakkoIndex - HidariKakkoIndex +1); KeisanGo := OneKakkkoEnzan(KeisanMae); Suushiki := StringReplace(Suushiki, KeisanMae, KeisanGo, []); end; Result := StrToFloat(StringReplace(Suushiki, '|', '', [])); end; end.