前提・実現したいこと
前々から、「半角の「数字、正負記号、小数点」以外を受け付けず、かつ、桁数制限のある関数」を作成したいと思っており、ついに完成しました。これをさらに安全高速綺麗にしたいです。ご助言願います。
###仕様
文字コードにおいて数字0~9は、0から始まり連続して存在しているとする。(そうでない環境なんてレアらしいし。)
与えた因数で桁数を制限する
返り値は成功した場合は入力した文字列をdouble型に変換したもの。
失敗は今のところ定義されていない。入力にミスがあった場合は入力処理を繰り返すようになっている。
char型配列dateの大きさを越える入力があるとオーバーフローする。
ソースコード
C
1//http://stroll.hatenablog.com/entry/2015/07/20/221125 を基に開発 2#ifndef imputtestfunc//二重でincludeされることを防ぐ 3#define imputtestfunc 4 5#include <stdio.h> 6#include <string.h> 7#include <math.h> 8 9double dnkscan(int lim) { 10 char data[256]; 11 int i,j, intp = 0,decp=0, nsign = 1, flag = 0, isdecimal=0; 12 double dnam; 13 //printf("[db]入力文字列が「半角数値及び正負記号,小数点」の時はその値を返します。それ以外ははエラーとなります。\n"); 14 //printf("[db]%d桁以内の数値を入力してください。>>\n",lim); 15 do{ 16 intp=0;decp=0;nsign=1;flag=0;isdecimal=0; 17 18 //fgets(data,lim,stdin);//new これだと入力許容桁数で自動的に切り詰められてOK判定が出てしまう。 19 scanf("%s", data) ;//old これだと、オーバーフローの可能性が捨てきれない。 20 if (strlen(data) > lim) { 21 flag = 2; 22 } 23 i = 0;j = 0; 24 if (flag == 0 && data[0] == '+') { 25 i++; 26 } 27 if (flag == 0 && data[0] == '-') { 28 nsign = -1; 29 i++; 30 } 31 32 //printf("[db]flag is %d data[i] is %c isdecimal is %d j is %d\n",flag,data[i],isdecimal,j); 33 for (; i < strlen(data); i++) { 34 //printf("[db]flag is %d data[i] is %c isdecimal is %d j is %d\n",flag,data[i],isdecimal,j); 35 36 if (flag == 0 && data[i] == '.'&&isdecimal==0) { 37 isdecimal = 1; 38 //i++;//いるか? 39 } 40 41 else if (flag == 0 && (data[i] >= '0' && data[i] <= '9')&&isdecimal==0) { 42 intp = intp * 10 + data[i] - '0'; 43 //printf("[db]整数部シーケンス intp=%d\n",intp); 44 } 45 else if(flag == 0 && (data[i] >= '0' && data[i] <= '9')&&isdecimal==1) { 46 decp = decp * 10 + data[i] - '0'; 47 //printf("[db]小数部シーケンス decp=%d\n",decp); 48 j++; 49 } 50 else{ 51 if(flag == 2) { 52 printf("桁あふれエラー>>\n"); 53 break; 54 }else{ 55 printf("エラー>>\n"); 56 flag = 1; 57 break; 58 } 59 } 60 } 61 //printf("[db]flag is %d data[i] is %c isdecimal is %d j is %d\n",flag,data[i],isdecimal,j); 62 if (flag == 0) { 63 dnam = nsign*( intp+ decp*pow(0.1,j) ); 64 //printf("成功:%lf\n", dnam); 65 } 66 }while (flag!=0); 67 ///memset(data, '\0', sizeof(data) ); 68 //printf("[db]date=[%s]",data);//ちゃんとリセットかかっているかの点検用 69 return dnam; 70} 71#endif
###やってみたこと
fgets使ってみましたが、あふれた桁を読み飛ばして、成功としてしまうので、これはどう回避したものか。
###余談
関数名は double型のnamberをscanするkazumori製関数という意味で付けました。もっといい感じの名前も募集中。
###19/02/13追記
変換部分の試行錯誤として、C言語関数辞典のstrtodを参考に作ってみようと、サンプルコードを試してみましたが求める結果が出ませんでした。関数の仕様でしょうか?
c
1#include <stdio.h> 2#include <stdlib.h> 3 4/* macros */ 5#define N 256 6 7/* main */ 8int main(void) { 9 char s[N] = {'\0'}, *endptr; 10 double x; 11 12 /* 入力 */ 13 fgets(s, N, stdin); 14 printf("変換前: %s", s); 15 16 /* 変換 */ 17 x = strtod(s, &endptr); 18 printf("変換後: %.2f\n", x); 19 printf("endptr: %s\n", endptr); 20 21 return EXIT_SUCCESS; 22}
変換前: 123456789012345678901234567890 変換後: 123456789012345680000000000000.00 求結果: 123456789012345678901234567890.00