質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

1782閲覧

【C言語】特定の文字の下に「^」を表示させる方法

tarasawa

総合スコア17

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2017/01/23 00:20

###前提・実現したいこと
16進数の電卓を作っている。
基本的な機能は計算式を入力して、結果を表す。(括弧付き計算式はOK)
入力した不正な数式の不正箇所を「^」で指摘したいが、やり方が分からない。
例:5+((6/2)--1
| ^ ^

一応、ソース全体を張り付けておく。

###該当のソースコード

#include <stdio.h> #include <string.h> #include <stdlib.h> #define MaxSize 32 char str[MaxSize]; int sign; void translate(char str[],char exp[]) {//逆ポーランド記法に直す struct{ char data[MaxSize]; int top; }op; char ch; int i=0; int t=0; op.top=-1; ch = str[i]; i++; while(ch!='\0'){ switch(ch){ case '(': op.top++; op.data[op.top]=ch; break; case ')': while(op.data[op.top] != '('){ exp[t]=op.data[op.top]; op.top--; t++; } op.top--; break; case '+': case '-': while(op.top != -1&&op.data[op.top] != '('){ exp[t] = op.data[op.top]; op.top--; t++; } op.top++; op.data[op.top] = ch; break; case '*': case '/': while(op.data[op.top] == '/'||op.data[op.top] == '*'){ exp[t] = op.data[op.top]; op.top--; t++; } op.top++; op.data[op.top] = ch; break; case ' ': break; default: while((ch >= '0'&&ch <= '9')||(ch >= 'a'&&ch <= 'f')){ exp[t] = ch; t++; ch = str[i]; i++; } i--; exp[t] = '#'; t++; } ch = str[i]; i++; } while(op.top != -1){ exp[t] = op.data[op.top]; t++; op.top--; } exp[t] = '\0'; } int cal_value(char exp[],int* flag){ struct{ signed int data[MaxSize]; int top; }st; signed int d; char ch; int t = 0; st.top = -1; ch = exp[t]; t++; while(ch != '\0'){ switch(ch){//四則演算 case '+': st.data[st.top-1] = st.data[st.top-1]+st.data[st.top]; st.top--; break; case '-': if(st.top<=0){ st.data[st.top]=-st.data[st.top]; return st.data[st.top]; break; }else{ st.data[st.top-1] = st.data[st.top-1]-st.data[st.top]; } st.top--; break; case '*': st.data[st.top-1] = st.data[st.top-1]*st.data[st.top]; st.top--; break; case '/': if(st.data[st.top] != 0) st.data[st.top-1]=st.data[st.top-1]/st.data[st.top]; else{ printf("除数を正しく入力してください\n\n"); *flag=1; } st.top--; break; default: d=0; while(ch !='#'){//文字列を数値に if(ch>='0'&&ch<='9'){ d = 16*d+ch-'0'; ch = exp[t]; t++; }else if(ch>='a'&&ch<='f'){ d = 16*d+ch-0x57; ch = exp[t]; t++; } } st.top++; st.data[st.top] = d; if(sign==1){ st.data[0]=-st.data[0]; sign=0; } } ch = exp[t]; t++; } return st.data[st.top]; } int CheckLen(char data[]){ int i=0; for(i=0;data[i]!='\n';i++){ if(i>32){ printf("最大入力文字数を超過しました。\n30文字以内でもう一度入力しください。\n\n"); return 1; } } if(data[0]==10&&data[1]==0){ printf("計算式が入力していません。\nEnterキーを押して、もう一度計算式を入力してください。\n\n"); return 1; } return 0; } int DeleteSpaeceTab(char *data){ int i=0; int speace_tab_cnt=0; while(data[i]!='\0'){//タブ・スペースの削除 if(data[i]==' '||data[i]=='\t'){ while(data[i]!='\0'){ data[i]=data[i+1]; i++; } i=0; } i++; } data[i-1]='\0'; return *data; } int MaxMinCheck(char *data){ int operator_flag=0; int i=0; int j=0; char w[31]={' '}; long long w2=0; char *extra; while(data[i]!='\0'&&i<=32){ if(data[i]=='+'||data[i]=='-'||data[i]=='*'||data[i]=='/'){ operator_flag=1; } i++; } i=0; if(operator_flag==1){ while(data[i]!='\0'){//最大値チェック(演算子がある場合) if(data[i] >= '0'&& data[i] <= 'f'){ w[j]=data[i]; i++; j++; }else{ w2=strtol(w,&extra,16); if(w2<INT_MIN||w2>INT_MAX){ printf("入力可能な桁数は8桁で、範囲は80000000~ffffffffです。\n\n"); return 1; break; } i++; j=0; } if(j>8){ printf("入力可能な桁数は8桁で、範囲は80000000~ffffffffです。\n\n"); return 1; break; } } }else{ i=0; while(data[i]!='\0'){//最大値チェック(演算子がない場合) w[i]=data[i]; i++; } w2=strtol(w,&extra,16); if(i>8){ printf("入力可能な桁数は8桁で、範囲は80000000~ffffffffです。\n\n"); return 1; }else if(w2<INT_MIN||w2>INT_MAX){ printf("入力可能な桁数は8桁で、範囲は80000000~ffffffffです。\n\n"); return 1; } } return 0; } int IncrrectFormulaCheck(char *data){ int i=0; while(data[i]!='\0'){//不正数式の例外処理 if(data[i]=='/'&&data[i+1]=='0'){ printf("除数を正しく入力してください。\n\n"); return 1; break; }else if(data[i]=='+'||data[i]=='-'||data[i]=='*'||data[i]=='/'){ if(data[i+1]=='+'||data[i+1]=='-'||data[i+1]=='*'||data[i+1]=='/'){ printf("エラー!演算子を正しく入力してください。\n\n"); return 1; break; }else{ i++; } }else if(data[0]=='*'||data[0]=='/'){ printf("エラー!計算式を正しく入力してください。\n\n"); return 1; break; }else if(data[i]=='('||data[i]==')'){ i++; }else if(data[i]>= '0'&&data[i] <= '9'){ i++; }else if(data[i]>='a'&&data[i]<='f'){ i++; }else if(data[i]>='A'&&data[i]<='F'){ printf("英字は小文字で入力してください。\n\n"); return 1; break; }else{ printf("エラー!計算式を正しく入力してください。\n\n"); return 1; break; } } i=0; while(data[i+1]!='\0'){//末尾文字の要素番号を計算 i++; } if(data[i]>='0'&&data[i]<='9'){//末尾の不正数式指摘 }else if(data[i]>='a'&&data[i]<='f'){ }else if(data[i]=='('||data[i]==')'){ }else{ printf("不正な数式が入力されました。\n\n"); return 1; } return 0; } int main(void){ char str[MaxSize],exp[MaxSize]; int i=0; int j=0; int k=0; int err_flag=0; int err_flag2=0; printf("*******************************************\n"); printf("\t\t十六進数電卓\n"); printf("*******************************************\n\n"); while(1){ memset(str,-2,sizeof(str)); memset(stdin,0,sizeof(stdin)); err_flag=0; err_flag2=0; i=0; printf("計算式を入力してください。\n"); //gets_s(str,32); fgets(str, 32, stdin); if(str[0]=='z'&&str[1]==10&&str[2]==0){ exit(1); }else if(str[0]=='z'){ printf("終了したい場合はzを入力し、Enterキーを押してください。\n\n"); err_flag=1; }else{ err_flag=CheckLen(str); if(err_flag!=1){ DeleteSpaeceTab(str); err_flag=MaxMinCheck(str); if(err_flag!=1){ err_flag=IncrrectFormulaCheck(str); } } if(str[0]=='-'){//負数対応 while(str[i]!='\0'){ str[i]=str[i+1]; i++; } sign=1; }else if(str[0]=='+'){ while(str[i]!='\0'){ str[i]=str[i+1]; i++; } sign=0; } } if(err_flag!=1){ translate(str,exp); k=cal_value(exp,&err_flag2); if(err_flag2==0){ printf("=%x\n",k); printf("\n"); } } if ( str[strlen(str)-1] != '\n' ){ while( getchar() != '\n' ); } } system("pause"); return 0; }

###試したこと
まだ何も試していない

###補足情報(言語/FW/ツール等のバージョンなど)
C言語/Microsoft Visual C++ 2010/.NET 4.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

WoodenHamlet

2017/01/24 01:57

パッと見ですが str を大域変数にしたなら(大域変数にするにしてももう少し大域変数らしい変数名にした方が良いです。ハンガリアン記法にしろとは言わないけど)いちいち関数に渡すのはやめましょう。
guest

回答3

0

ベストアンサー

char Error[入力された文字数+1]
と言う文字列を全て空白で埋めます。

文法チェックしている関数でエラーを検出したらError[i]='^';
としてあげる。

投稿2017/01/23 00:38

hikochang

総合スコア648

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tarasawa

2017/01/23 06:39 編集

 仰ったやり方でやってみました。今回のロジックは、不正箇所を一つ検出するとすぐにエラーを出すので、「^」で複数不正箇所を指摘するには、おそらくIncrrectFormulaCheck()の全体的な改変が必要です。理論上は、実現できると思います。ヒントをいただいて、ありがとうございます。  主旨から少し逸れますが、今char error[MaxSize]を「^」を格納する配列として実装しています。宣言した直後に「memset(error,' ',sizeof(error))」で初期化しています。にも関わらず、不正箇所の要素に「^」を入れ後、配列全体を表示しようとすると、右側に変な記号の文字列が入っています。どうすれば、配列を綺麗に初期化できますでしょうか。
hikochang

2017/01/23 14:55 編集

c言語の文字列の扱いはNULL終端にする必要が有ります。 最後の文字の後ろに'¥0'(NULL)が必要です。'¥0'が無いと文字列が終わったとは認識してくれないです。あまりスマートでは無いですが、^を代入するところで以下のようにすると解決できます。 error[i]='^'; error[i+1]='¥0'; //文字列の終了
WoodenHamlet

2017/01/24 00:46 編集

errorを初期化した後、最後の要素にNULL文字(NULLではないことに注意)を代入すればいいと思いますよ。 ソース見てないので憶測になりますが、"^"代入後次の要素をNULL文字では、"^"が複数個、離れた位置にある場合最初の"^"しか表示されないのではと考えています
hikochang

2017/01/24 01:34

WoodenHamletさん 1つ目のエラーで処理が終了するようです。
WoodenHamlet

2017/01/24 01:52

ああそういうことでしたか。 ただ、(文字列として使用するつもりの)char配列が終端されていないタイミングがあるというのがきな臭く感じてしまうので、どちらにせよ初期化時にいったん終端しておいた方が、バグが発生しにくいのではとは思います。
hikochang

2017/01/24 02:20

そう思います。 さらに言うとchar配列にmemsetは嫌いなので、気の利いた提案が出来ませんでした。 文字列として扱う癖をつけた方がよさそうですが、今回はエラーで処理終了なので文字列も終了と提案しました。
tarasawa

2017/01/25 05:25

hikochangさん、WoodenHamletさん、ありがとうございます。 変な記号の問題は解決しました。 これ↓で成功しました。 error[i]='^'; error[i+1]='¥0'; //文字列の終了 ありがとうございます。
guest

0

C#で16進電卓を100行以内を目標に作ってみました。動作が正しいか確認していませんし、1/3*3などを行うと0.9999999になるなど当然精度や性能は考えていません。
実は、16進数を10進数に変換した段階でWindows PowerShellに計算を任せてしまうと、30行程度で16進数関数電卓は作れる気がしますが、今回はカッコと四則演算の優先度を自分で実装してみました。

C#(.NET)や正規表現という世界もあるという事をお伝えしたく、、、ご参考まで

C#

1using System; 2using System.Linq; 3using System.Text.RegularExpressions; 4 5namespace HexCalc 6{ 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 while (true) 12 { 13 string input = Console.ReadLine(); 14 // エラーチェック 15 Match match = Regex.Match(input, 16 @"((?<=[0-9a-zA-Z][ \t]*)\()|" + // "3(":数字直後のカッコ開始 17 @"((?<=[\+\-\*\/][ \t]*)\))|" + // "+)":演算子直後のカッコ終了 18 @"((?<=[\+\-\*\/][ \t]*)[\*\/])|" + // "+/":加減算の直後の乗除算 19 @"([\+\-\*\/][ \t]*[\+\-\*\/][ \t]*[\+\-\*\/])|" + // "*-+":演算子の3連続 20 @"((?<=\/[ \t]*)0(?![0-9a-zA-Z]))|" + // "/0 ":ゼロ割 21 @"((?<=^[ \t]*)[\*\/])|" + // "^*":先頭の乗除算 22 @"([\+\-\*\/](?=[ \t]*$))|" + // "+$":末尾の演算子 23 @"(^[ \t]*$)"); // "":空 24 if (match.Success) 25 { 26 Console.WriteLine(input); 27 Console.WriteLine(string.Concat(Enumerable.Repeat(" ", match.Index)) + "^"); 28 continue; 29 } 30 31 // カッコの対応チェック 32 int level = 0; 33 int i = 0; 34 for( i=0; i< input.Length; i++ ) 35 { 36 level += (input[i] == '(') ? +1 : 0; 37 level += (input[i] == ')') ? -1 : 0; 38 if (level < 0) 39 { 40 break; 41 } 42 } 43 if (level != 0) 44 { 45 Console.WriteLine(input); 46 Console.WriteLine(string.Concat(Enumerable.Repeat(" ", i)) + "^"); 47 continue; 48 } 49 match = Regex.Match(input, @"(?<![0-9a-zA-Z])[0-9a-zA-Z]+(?![0-9a-zA-Z])"); 50 while (match.Success) 51 { 52 input = Regex.Replace(input, match.Value, Int64.Parse(match.Value, System.Globalization.NumberStyles.AllowHexSpecifier).ToString()); 53 match = match.NextMatch(); 54 } 55 56 input = '(' + input + ')'; 57 while ((match = Regex.Match(input, @"\([ \t\+\-\*\/\d\.]*\)")).Success) 58 { 59 string result = match.Value.Trim(new char[] { '(', ')' }); 60 Match m; 61 double r; 62 while ((m = Regex.Match(result, @"(?<=(^|[\+\-\*\/])[ \t]*)[\+\-]?\d+(\.\d+)?[ \t]*[\*\/][ \t]*[\+\-]?\d+(\.\d+)?(?!\d)")).Success) 63 { 64 if (0 <= m.Value.IndexOf("*")) 65 { 66 string[] op = m.Value.Split('*'); 67 r = double.Parse(op[0]) * double.Parse(op[1]); 68 } 69 else 70 { 71 string[] op = m.Value.Split('/'); 72 r = double.Parse(op[0]) / double.Parse(op[1]); 73 } 74 result = Regex.Replace(result, Regex.Escape(m.Value), r.ToString()); 75 } 76 while ((m = Regex.Match(result, @"(?<=(^|[\+\-\*\/])[ \t]*)[\+\-]?\d+(\.\d+)?[ \t]*[\+\-][ \t]*[\+\-]?\d+(\.\d+)?(?!\d)")).Success) 77 { 78 MatchCollection op = Regex.Matches(result, @"(?<=(^|[\+\-\*\/])[ \t]*)[\+\-]?\d+(\.\d+)?[ \t]*(?!\d)"); 79 if (0 <= m.Value.IndexOf("+")) 80 { 81 r = double.Parse(op[0].Value) + double.Parse(op[1].Value); 82 } 83 else 84 { 85 r = double.Parse(op[0].Value) - double.Parse(op[1].Value); 86 } 87 result = Regex.Replace(result, Regex.Escape(m.Value), r.ToString()); 88 } 89 input = Regex.Replace(input, Regex.Escape(match.Value), result); 90 } 91 Console.WriteLine("10進数:"+input); 92 } 93 } 94 } 95}

実行結果

a+(b+(d*(e+f))))+1
^
a+(b+(d*(e+f)))
10進数:398

投稿2017/01/25 14:15

編集2017/02/01 08:02
hikochang

総合スコア648

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hikochang

2017/01/25 16:08

C#でPowerShellを使う場合の16進数電卓、答えは10進数です、、、 38行でした。ちゃんと1/3*3が1になります。 using System; using System.Text.RegularExpressions; using System.Management.Automation; // using System.Management.Automation; を使用するためには以下を参照する必要がある。 // C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll namespace HexCalc { class Program { static void Main(string[] args) { while (true) { string input = Console.ReadLine(); try { Match match = Regex.Match(input, @"(?<![0-9a-zA-Z])[0-9a-zA-Z]+(?![0-9a-zA-Z])"); while (match.Success) { input = Regex.Replace(input, match.Value, Convert.ToUInt64(match.Value, 16).ToString()); match = match.NextMatch(); } using (var invoker = new RunspaceInvoke()) { var result = invoker.Invoke(input, null); Console.WriteLine("10進数:" + result[0]); } } catch (Exception e) { Console.WriteLine(e.Message); } } } } }
guest

0

具体的にどの辺りが判らないのか質問文からサッパリかつソース丸投げなんでアレですが(まさか関数丸ごと作れとか言ってるわけじゃないとは思いたい)、incorrect 関数のできていない部分を見るに、括弧の対応が取れているかをチェックできていないとかなんかその辺が解っていないのかな。

せっかく計算式文字列の最初から舐めてるんだから、"("が出てくるごとに+1する変数をつくって、")"が出てくるごとに―1する変数を作ればいいんじゃないかな。
途中で負の数になれば")"が過剰だとわかるし、最後まで来たのに1以上だったなら"("が過剰だ。

ついでに、incorrect 関数最後のパートでi=0にして最後の文字がどこか数えているけど、それ以前の部分で文字数は判明していると思うけどどうだろうか。

(追記)
警告はちゃんと見ていますか?まともなコンパイラを使っているなら少なくとも DeleteSpaeceTab 関数の返り値の型が違うと文句を言ってくるはずです。

投稿2017/01/24 06:19

編集2017/01/25 03:00
WoodenHamlet

総合スコア306

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tarasawa

2017/01/25 05:04

>せっかく計算式文字列の最初から舐めてるんだから、"("が出てくるごとに+1する変数をつくって、")"が出てくるごとに―1する変数を作ればいいんじゃないかな。 >途中で負の数になれば")"が過剰だとわかるし、最後まで来たのに1以上だったなら"("が過剰だ。 まずは当方はプログラミング学習歴半年もない初心者です。初めての計算器プログラムなので、作り方が何一つも分かっていない状態で始めました。様々な異常系はまだ網羅できていないです。 仰っている問題点と解決策は理解できています。これから実装してみます。 >ついでに、incorrect 関数最後のパートでi=0にして最後の文字がどこか数えているけど、それ以前の部分で文字数は判明していると思うけどどうだろうか。 設計ミスです。確かに文字を数え直す必要はないです。 >警告はちゃんと見ていますか?まともなコンパイラを使っているなら少なくとも DeleteSpaeceTab 関数の返り値の型が違うと文句を言ってくるはずです。 何も警告されていないですよ。WoodenHamletさんはコンパイラ何を使っていますか。
WoodenHamlet

2017/01/25 06:09

ああすいません、コピペミスってしまっていました。警告の件は無視してください
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問