C#で巨大な文字列の計算をさせる方法

このQ&Aのポイント
  • C#にて、巨大な桁数を持つ文字列の計算を行いたい場合、decimal型ではなくdouble型を使用する必要があります。DataTable.Computeメソッドを使って計算を行うことができます。
  • 計算式を文字列として受け取り、DataTable.Computeメソッドを使用して計算を行います。しかし、decimal型で計算結果を取得する場合には、戻り値をdouble型として取得し、キャストを行う必要があります。
  • intやdoubleでは収まらない巨大な桁数を持つ文字列の計算を行いたい場合には、double型を使用し、DataTable.Computeメソッドを利用することで計算を行うことができます。
回答を見る
  • ベストアンサー

C# 巨大な文字列の計算をさせたい

お世話になります。 C#にて、文字列からなる計算式 string s = "(3270+(5*4))/7"; のようなものを計算して 値を返す処理を作成したいと思っています。 http://dobon.net/vb/dotnet/programing/eval.html ↑上記のサイト様から、DataTable.Computeで 求める方法を参考にしたのですが、戻り値を decimalで受けたいのですが、decimalで取得することができません。 doubleでは取得できますが、decimalでcastしようと すると、InvalidCastExceptionが発生します。 string exp = "(1+6)*5/(7-4)"; //式を計算する System.Data.DataTable dt = new System.Data.DataTable(); decimal result = (decimal)dt.Compute(exp, "");        ↑この行で発生 要は、intやdoubleでは収まらない巨大な桁を持つ文字列の 計算をさせたいのですが、どうすればよいでしょうか。 よろしくお願いいたします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.1

decimal result = decimal.Parse (dt.Compute(exp, "").ToString());

tatapatank
質問者

お礼

よくよく考えてみべましたら、桁があふれるほどのものは 要求する必要がなく、事前に文字列の長さを測って、 長ければ受け付けないようにすればいいのだと気がつきました。 いろいろな方から回答をいただきましたが、本件に一番近い 回答でしたので、ベストアンサーにさせていただきます。 お騒がせいたしました。

tatapatank
質問者

補足

回答ありがとうございます。 惜しいところまでは行くのですが、計算結果が 指数("1.234560E+002")等の答えになった時に、変換できないと 例外が出てしまいます。

その他の回答 (4)

  • arihina
  • ベストアンサー率26% (4/15)
回答No.5

この回答はまともな解答になっているかどうか。。。 まず、Computeが何のオブジェクトを返しているか、デバッグして確かめて見る必要があるかと思います。もし、Int32だったら、もうそれまでですし。 あるいは、sの計算式にSQLのdecimalへのキャスト関数のようなものをつけるという方法もあるでしょう。そうすれば、decimalで返ると思います。 最悪、多倍長精度計算のライブラリをどこかから探してくるという方法も考えられます。その場合、自分で式を計算するプログラムを組む必要があるでしょう。でも、decimalでいいんですよね?

tatapatank
質問者

補足

回答、ありがとうございます。 計算結果は、decimalであれば十分だと思っています。 普通にdoubleで拾えるのだから、decimalででも拾えるかと思っていたのですが、 なかなかうまくいかないみたいです。 数値が指数にさえならなければ、No.1さんの答えでも 上手くいったのですが、そこがクリアできればと 思うのですが…。

回答No.4

たとえば 1 / 3 の結果は"無限の桁"となりますが、いかがいたします?

tatapatank
質問者

補足

そうですよね。 電卓に似せようと思っているので、結果的には 『0.33333333333333333334』などのようにしたいと 思います。 こういう場合もあるんですよね。 ご指摘、ありがとうございます。

  • Glory_777
  • ベストアンサー率50% (105/208)
回答No.3

64ビットで受けるならば、Int64と言う型があったと思います。 doubleは通常8バイト精度だったはずです。 ですので通常の型変換でも32ビットのInt32のサイズだと、警告がますね。 ライブラリの使い方などは、製作者に依存しますので、 私の方では、お答えできませんが、 仕事でExcelのデータを読み出して、シートの内容を再計算するライブラリを C#で書き下したことがあります。 意外と簡単で、3時間くらいで組みあがりましたよ。 そこで、今回はこれの作り方など、考え方をご紹介します。 C#はC言語に比べて生産性が高いので、スピードを要求するような処理以外なら、 手製でライブラリを作成しても早いです。 では、 数式を分解して演算するライブラリの自作について考えて見ましょう。 Step1)数値表現をするクラスを作る 通常はデフォルトで用意されている、double、int などの型を使いますよね。 しかし、ご質問のテーマでは膨大な桁をもつ数値を取り扱うわけです。 ですのでこのStep1がどうしても必要になります。 数値型をデフォルトで用意されていると、恐れ多く感じますよね。 自分で作れないと思ってしまいます。 ですが良く考えると、幾つかのメソッドがあれば事足ります。 ここでは、 "1234567891234567891234567891234"のような数値を取り扱えるクラスを考えます。 プログラム表現は長くなるので割愛します。 一つ一つは簡単なループ文で作成できると思いますので、デバッグをしっかりやれば、 ちゃんと動くと思います。 では、メンバとメソッドを定義します。 以下がクラスの概要です。 class OriginalValue ○メンバ ・string Value; 文字列表現として保存しておくための string 領域 ・double[] val; 演算をするための doulbe[] 配列領域 簡単のために10進で桁単位に格納します。 ・int floatIndex; 小数点以上、と小数点以下のが何桁目であるか、valのインデックスで記録します。 ・string Error; 0での除算等を発見した場合、ここにエラーを書いておきます。  exp) "1234.56" ⇒ [6.0][5.0][4.0][3.0][2.0][1.0] , floatIndex = 2;  格納順は自分で決めておきましょう。  このあとで、  小学校の算数と同じように、四則演算での桁上がりを行って、四則演算行うメソッド  を実装します。簡単なループ文でのメソッドを4つ作るだけです。 ここで二つのOriginalValueを使って演算するとき、小数点の位置を先に意識します。  対応する桁同士を演算します。これにより、小数点も扱うことが出来ます。 ○メソッド ・OriginalValue( string val ) コンストラクタ1、  string で初期値を指定して、double[]演算領域,floatIndexに格納する ・OriginalValue( double val ) コンストラクタ2、  val を10で割りながら、double[]演算領域,floatIndexに結果を格納する  ・Set( string val )  コンストラクタ1と同様処理 ・Set( double val )  コンストラクタ2と同様処理 ・ToString()  演算領域を文字列(小数点も意識)になおして、string領域に格納し、それを返却する ・Add( OriginalValue val ) 桁上がりの足し算演算をfor文などを使ってプログラムする ・Sub( OriginalValue val ) 桁上がりの引き算 ・Mul( OriginalValue val ) 桁上がりの掛け算 ・Div( OriginalValue val ) 桁を考慮した掛け算 Step2) 書式解析を行うクラスを作る "(34578881003455 + ( 56633566565.0 ) / (565655655) )" の様な演算式を解析し、結果をOriginalValueで返却します。 (もちろんこれを後でOriginalValueのメソッドに加える) 書式の解析は段階をおいておこないます。 括弧があると、その中の演算を先にしないといけません。 そのため再帰処理(自分自身をコールする)を使用します。 また、括弧が無い場合でも、乗除算は先にしないといけません。 そのため、最初に'+'や'-'記号で文字列をスプリットし、 その中身を更に乗除算で先に処理します。 戻り値が数値の文字列であれば、コレを使って置換してしまうことで、 数式を表す文字列が徐々に数値を表す文字列に変換されていきます。 以下のアルゴリズムになります。 (1)書式の頭から'('を探し、見つけたら、')'までを抜き出し、自分自身を再帰コールする  ただし、ネストがありうるので、'('の数を数える等して、対応する')'を見つけ、  '('でネストしている文字列を正確に抜き出します。  戻ってきた値(数値を表す文字列:OriginalValueのToStiring()で返却)  で、'('~')'までを文字列置換し、この書式自体を直してしまいます。  '(’がネストされていても再帰中の(1)で処理され、最後は数値になっているはずです。  最後まで到達したら、変換された書式結果を使って(2)へ進みます。 (2)(1)の処理を通過(つまり'('がない書式)した次は、以下の四則演算ルーチンをコールする  四則演算ルーチン概要:  A.加減算  ・ 四則演算ルーチンのコール先です。最初に加減算で分解します。    ・ '+' , '-' で文字列をスプリットします。この演算記号も取っておきます(※1)  ・ スプリットしても文字列配列要素が一つである場合は、    処理せずにそのまま入力を返します。    このとき数値ではない文字列、””や"-",","-"などの記号である場合は、    ”0”を返却します。  ・ スプリットされた文字列を B.乗除算ルーチンに渡し、数値文字列に変換してもら    います。B.乗除算ルーチンの戻り値で、分離した文字列配列の要素を置換します。  ・ スプリットされた文字列全てが数値文字列に変換された場合、  ・ ここでは※1でとって置いた通りの加算、減算を順番に行います。    このときOriginalValue のAdd,Subメソッドを使用します。    最終値を数値文字列として返却します。  B.乗除算   ・ '*' , '/' で文字列をスプリットします。この演算記号も取っておきます(※2)  ・ スプリットしても文字列が一つである場合は、処理せずにそのまま入力を返します。    このとき数値ではない文字列、””や"-",","-"などの記号である場合は”0”を返却します。  ・ スプリットされた文字列に対して、OriginalValue のMul,Div    メソッドを使って演算をし、文字列を返却します。    0で割らないように気をつけます。0での除算を見つけた場合は、前述Errorエリアに    エラーメッセージ等を書き、処理を中断。戻り値は”0”で脱出します。    既に別の場所でエラーを検出している場合もあるので、上書きをしないようにします。  ・ エラーが無い場合は、※2でとって置いた通りの乗算、加算を順番に行っていきます。    最終値を数値文字列として返却します。 (3)四則演算ルーチンからの数値文字列を返却します。 Excelなどでは、ROUND(xx)などの様なマクロが発見されますが、 これに対応するためには、先に上記の書式解析演算プログラムを作り、 どこでマクロ文字列を抜き出せるか、ステップトレースすれば分かります。 ここで、マクロ判定を行い、自分で計算して値を返却すればよいのです。 こうすれば、どんなにマクロが増えてもその度に、そこだけ追加すれば対応できます。 便利なライブラリを探して使い方が分かるまで、結構な時間が掛かります。 DataGridなども、自作すると2日も掛かりません。 しかし、こういった高機能ライブラリは、自作で対応するよりも、 メソッドのオーバーライドなどを覚えておくと良いと思います。 C#は、ライブラリを自作すると言う意味で非常に優れています。 他のライトウェイト言語に進むと、表現力が乏しくなったり、出来ることが少なくなる 場合もあります。 また、C++から如何にして生産性を上げるかと言う点で、練りこまれています。 この思想は示唆に富んでおり、C++ベースで開発を行う場合も、 C#で用意されている同等のメソッドや仕組みを導入する事が出来ます。 相乗して効果があるので大変勉強になりますよ。 使い方を調べて、質問をしていると、ここで差が付いてしまいます。 「自分で作ったら意外と簡単だった」 この喜びが、ソフトウェア作りの原動力です。 楽しんで学んで、楽しんで糧を得ましょう。 以上、ご参考になれば

tatapatank
質問者

お礼

丁寧なご回答、ありがとうございます。 既存の方法でできるのであればと思っていたのですが、 やはりそこまではうまくいかないみたいです。 でも、教えていただいた方法も試してみたいと思います。 参考にある方法、ありがとうございました。

  • wormhole
  • ベストアンサー率28% (1619/5654)
回答No.2

>doubleでは取得できますが、decimalでcastしようとすると、InvalidCastExceptionが発生します。 DataTable.Computeの戻り値のインスタンスがdecimalにキャストできるようなクラスでないのでしたらInvalidCastExceptionは当然発生します。 >要は、intやdoubleでは収まらない巨大な桁を持つ文字列の計算をさせたいのですが、どうすればよいでしょうか。 そのような式を表す文字列の評価を行ってくれるメソッドを自分でつくる。 >http://dobon.net/vb/dotnet/programing/eval.html で参考にされたのは、その自分で作るというのをしないで楽するための方法です。 最初の方には「自分で解析する方法」というのも書かれているでしょ?

関連するQ&A

  • C#で型変換

    DataTable T_DATATABLE = new DataTable(); DataRow[] T_DATATABLE_row; DataAdapter adp = new DataAdapter("SELECT id,Date FROM TABLE", CONN); adp.Fill(T_DATATABLE); //データテーブルにidとDateフィールドがあります。 //dtに下記で取得したダータを入れようと思っているのですが、 //System.Datetimeに変換することはできませんと表示されます。 DateTime dt = T_DATATABLE.Rows[0]["Date"]; どのようにすれば、型変換をすることができるでしょうか? string dt = (string)T_DATATABLE.Rows[0]["Date"]; string dt = (string)T_DATATABLE.Rows[0]["Date"].toString; としてもできません・・・ ご教授お願い致します!

  • C#で、文字列の内容の計算をさせる方法

    お世話になります。 C#で、文字列の中に書かれている式を計算して値を出してくれる 関数、もしくは、それに近い方法を探しています。 たとえば、 string st = "3+2*5-6"; という文字列を渡すと、『7』を返してくれるような関数です。 あるのであればその関数を、ないのであれば、似たようなことが できる方法を教えてください。 宜しくお願いします。

  • C言語で取得した文字列を、C++の文字列として取り扱いたいです。

    C言語で取得した文字列を、C++の文字列として取り扱いたいです。 皆さんこんにちは。 C言語で取得したchar型で定義された文字列を、 C++の「std::string」に渡したいと思っております。 どうすればかなうでしょうか? 具体的には次のような内容です。 ■C言語側 ---- char key1 = "deperture"; ---- ■C++側で「key1 = key2」としたいです。 ---- std::string key2 = key1; ---- C言語側で記述されている「key1」の値(deperture)を、 C++ソース内の「key2」に渡したいと思っています。 こうゆう場合、どんな方法をとれば適切でしょうか。 C言語側でのchar型の文字列の値を、 C++側の「std::string」型として C++のソースへ渡す方法が分からないです。 アドバイスいただけるとありがたいです。 簡単な例を頂けると更に大変ありがたいです。 以上どうぞ宜しくお願い致します。

  • [JAVA]evalで文字列を計算式に!

    こんにちわぁ♪ Javascriptに「eval」というコマンドがあると思います。 これは、文字列を計算式として変換するものです。 例えば String SHIKI = "A + B" という文字列があり、evalを使ってやると その時点で文字列ではなくなり、 普通に、 A=3; B=4; String C = eval(SHIKI); と計算式にすることができます(使い方がちがうかもしれませんが)。 しかし、JAVAスクリプトではあるこのコマンドが JAVAでは見つかりません。 これと同等のコマンド、JAVAにありますか?

    • ベストアンサー
    • Java
  • 文字列について

    適当な文字列をキーボードから入力し、次に別の適当な整数numをキーボードから入力する。次に先に入力した文字列のnum番目からnum個の文字だけ逆順になるように文字列を並び替えた文字列を新たな文字列変数に代入し、その結果を画面に出力するプログラムを下記で作成したのですが、文字列時代が逆になってしまいます。どうしたら、 結果    文字列を入力      My name is Taro Sandai.    整数を入力     10    My name isdnaS oraT になるのですか? import java.io.*; class mojiretu4 { public static void main(String args[]) throws IOException { System.out.println("文字列を入力"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str1 = br.readLine(); System.out.println("整数を入力"); String str2 = br.readLine(); int num = Integer.parseInt(str2); StringBuffer sb = new StringBuffer(str1); sb.reverse(); System.out.println(sb);

    • ベストアンサー
    • Java
  • 文字列の取得

    string.subString()のなどで文字列を指定文字数分取得する場合に、バイト数指定で取得する方法ってありますか? 教えてください。

    • ベストアンサー
    • Java
  • 文字列の算出について

    お世話になります。 文字列の10桁の基礎年金コードと 5桁の年金番号があったとします。 それと結合して123456789012345のように 15桁の文字列になるよう指示をだし その15桁から文字列の管理コード4桁を引き算したいのですが なにかアイデアはありますでしょうか?? 結合なしの引き算でしたら long型で宣言して 対応出来るのですが・・・ どうぞよろしくお願いいたします。 String 基礎コード = "1234567890"; String 年金番号= "12345"; String 管理コード= "5678"; System.out.println(String.format( )); System.out.println(管理番号 - 基礎年金番号);

    • ベストアンサー
    • Java
  • 文字列の分解・格納

    お世話になります。 文字列の分解についてお聞きします。 環境はVB.NET2008です。 Private Sub Main(ByVal CmdArgs() As String) Dim cmds() As String cmds = System.Environment.GetCommandLineArgs() End Sub コマンドライン引数で以下のように文字列を取得しています。 ("起動exe", "/KEY=○○○/ テーブル名=△△△") この文字列を元に、 変数Aに○○○を変数Bに△△△を代入したいのですが、どうやるのでしょうか? ご教示願います。

  • VB.NETで、配列をテーブルに変換する。

    VB.NETで、一次元配列string()にあるデータを、DataTableに変換 したいのですが、「型'String'の値を'System,Data,DataTable'に変換できません。」 と、エラーが発生してしまいます。 s1に、配列データが入っています。 Dim dt As DataTable Dim cnt As Integer Dim i As Integer dt = New DataTable count = s1.Length For i = 0 To cnt - 1 dt = CType(s1(i), DataTable) Next 解決方法が見つからず、行き詰っております。 宜しくお願い致します。

  • 数値を文字列に変換する?

    下のプログラムでなぜString ss= " "+dt;のように数値を文字列に変換しないとだめなんでしょうか? ちなみに桁数設定メソッドを作るプログラムです。 public class JcWidth { // JcWidth.java [java] public static void main(String args[]) { System.out.println(":" + toIntWidth(123, 8) + ":"); System.out.println(":" + toIntWidth(12345, 8) + ":"); System.out.println(":" + toIntWidth(-1234567890, 8) + ":"); System.out.println(":" + toHexWidth(4095, 8) + ":"); System.out.println(":" + toHexWidth(123456789, 8) + ":"); } // int値dtを先行空白付きw桁(最大12)の表現にする // 数値が指定幅より大きいときは必要な幅にする public static String toIntWidth(int dt, int w) { if (w > 12) w = 12; String ss = "" + dt; // 数値を文字列にする if (ss.length() > w) w = ss.length(); ss = " " + dt; return ss.substring(ss.length()-w); } // int値dtを先行0付きw桁(最大8)の16進数大文字表現にする public static String toHexWidth(int dt, int w) { if (w > 8) w = 8; String ss = Integer.toHexString(dt).toUpperCase(); if (ss.length() > w) w = ss.length(); ss = "0000000" + ss; return ss.substring(ss.length()-w); } } 

    • ベストアンサー
    • Java

専門家に質問してみよう