• ベストアンサー

atoiについての疑問

いつもお世話になっております。 またしても、教えていただきたい事がありまして質問させていただきます。 年月を『YYYY/MM/DD』形式で入力するプログラムで、『YYYY/MM/DD』形式以外の形式や暦として不正な値を入力すると、再度入力を促すプログラムを作成しています。 そこで、「strtok関数」を使い"/"で区切った文字列を「atoi関数」で数値にしようと思いました。(この方法で良いかは分からないのですが、他の方法が思い浮かばなかったので…) そこで、以下のようなプログラムを作ってみたのですが、結果を表示すると、なぜか最後の数字が1文字消えてしまいます。 ↓このようなソースを作りました。 【作ったソース】 #include<stdio.h> #include<stdlib.h> #include<string.h> void manth_view(void) { char nengetu[30],*syear; int year,manth; printf("年月を入力して下さい。>>"); fgets(nengetu,sizeof(nengetu),stdin); syear = strtok(nengetu,"/"); year = atoi(syear); printf("入力された年 %d\n",year); manth=atoi(strtok(NULL,"/")); printf("入力された月 %d\n",manth); } 【結果】入力した年月→2007/12  入力された年 200  入力された月 1 上記の結果のように、どのように入力しても、最後の1文字が消えてしまいます。 何故なのか昨日からずっと考えているのですが、分かりません。 どうか教えてください。

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

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.3

strtokもそうですが、atoiもあまり使用が推奨される関数ではないような… void month_view(void) { char nengetu[30]; char *p, *stp; int year, month; printf("年月を入力して下さい。>>"); fgets(nengetu, sizeof(nengetu), stdin); p = nengetu; year = (int)strtol(p, &stp, 10); printf("入力された年 %d\n", year); if (*stp!='/') { fprintf(stderr, "区切り記号が変です(%c)\n", *stp); return; } p = ++stp; month = (int)strtol(stp, &stp, 10); printf("入力された月 %d\n",month); } #1の方のstrstrを使ったやり方はstrtolだけでできますよ。

empuru
質問者

お礼

empuru様 丁寧なご回答をありがとうございます!! #1の方もおっしゃっていたのですが、strtokだけではなくatoiもあまり使わない方が良いのですね。 またしても勉強不足ですが【strtol】知りませんでした・・・。 調べてみると、エラー(数字以外)のチェックもできるのですね。 これは大変便利ですね。 早速ソースを書き直してみます。 とても勉強になりました! どうもありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (2)

  • FAY
  • ベストアンサー率49% (95/193)
回答No.2

WinXP上で動いているVC6.0のコンソールアプリケーションでは 示されたソースで正常に動作します。 他の原因があるのかも。

empuru
質問者

お礼

FAY様 説明足らずで申し訳ありません。 この部分だけを取り出すと動くのですが、関数としてmain関数などと共に動かすと、うまくいかないのです…。

全文を見る
すると、全ての回答が全文表示されます。
  • yukimican
  • ベストアンサー率70% (112/159)
回答No.1

まず、strtokとatoiのどちらに問題があるのか切り分けましょう。 strtokの直後で、printfで戻り値を表示して確認してみてください。 この時点で文字列が切れていたら、strtokが悪いことになります。 おそらくstrtokの方に問題があるのだと思いますが、 strtokはたまに挙動が怪しいときがあるので、なるべく使わない方が良いです。 こういうときに良く使う方法は (a)sscanfを使う(正否は関数の戻り値で判定) (b)strstr()とポインタを使って、自前で文字列を分解する  (0) 文字列の先頭をポインタpStrにセット  (1) pStrの中から、strstr() で文字('/')を検索、結果をポインタpSepにセット    → pSepがNULLだったらここで終了  (2) pSepが指している文字('/')を'\0'に置き換える  (3) pStrを atoi() で文字列→数値変換  (4) pSepの次のアドレスをpStrにセット    → pStrがNULLだったらここで終了  (5) (1)からもう1回

empuru
質問者

お礼

yukimican様 丁寧なご回答をありがとうございます。 教えていただいた通り、strtokの戻り値をprintfしてみたところ、文字列全体が表示できたことから、atoi関数の動きが悪いようです。以前はstrtokを使ったため、変な風になってしまったことがあるのですが、やはりstrtokは使わない方が良いのですね。 勉強不足でお恥ずかしいですが、【strstr()】を知りませんでした。 文字列から対象文字を検索できるのですね! ぜひ使ってみたいと思います! 本当にありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 間接操作のレベルとは

    C言語初心者です。 現在、yyyymmdd型での入力のテストを作っています。 しかし 「'convert_time':'tm *(char *)'は'int ()'と間接操作のレベルが異なります。」 とエラー表示が出てコンパイルできません。 プログラムは以下に示すとおりです。 #include <stdio.h> #include <time.h> char jikan; char a; int main (void){ printf("yyyymmdd型で入力してください"); scanf("%c",jikan); a = convert_time(jikan); printf("%s",a); } struct tm* convert_time(char* yyyymmdd){ struct tm* time; char yyyy[5]; char mm[3]; char dd[2]; strncpy(yyyy, yyyymmdd, 4); yyyy[5] = '\0'; mm[0] = yyyymmdd[4]; mm[1] = yyyymmdd[5]; mm[2] = '\0'; dd[0] = yyyymmdd[6]; dd[1] = yyyymmdd[7]; time = malloc(sizeof(struct tm)); time->tm_sec = 0; time->tm_min = 0; time->tm_hour = 0; time->tm_mday = atoi(dd); time->tm_mon = atoi(mm) - 1; time->tm_year = atoi(yyyy) - 1900; return time; } ご回答よろしくお願い致します。

  • C言語 atoi関数

    入力された年号と年数から(和暦)、西暦を算出するプログラムです。 年号の入力チェックのところが、どうしてもうまくいきません。  year_name = atoi( yn_buf ); year_name にatoi()で変換された値が入りません。 初期値のゼロのままです。その為、年号に何を入力してもエラーメッセージが表示されてしまいます。 atoi関数の使い方が間違っているのでしょうか? すみませんが、教えて下さい。 int year_name = 0; /* 年号を格納 */ int years = 0; /* 年数を格納 */ int check_flg = FALSE; /* 入力チェック 初期値としてエラー有状態とする*/ char yn_buf[32] = { '\0' }; /* 年号のチェック用 */ char y_buf[32] = { '\0' }; /* 年数のチェック用 */ printf( "<年号> 明治:m 大正:t 昭和:s 平成:h \n" ); printf( " 年号と年数を入力して下さい。 " ); scanf( "%s%s", &yn_buf, &y_buf ); printf("\n"); while( 1 ) /* 年号チェック */ { if( strlen( yn_buf ) >= 2 ) /* 年号が2文字以上の時、エラー */ { printf(" ▲エラーメッセージ 年号は1文字で入力して下さい。\n"); } else { year_name = atoi( yn_buf ); if( ( year_name == 'm' ) || ( year_name == 't' ) || ( year_name == 's' ) || ( year_name == 'h' ) ) { /* 年号が正しく入力されている時 */ break; } else { /* 年号が正しく入力されていない時 */ printf(" ▲エラーメッセージ 年号は、「 m, t, s, h 」のいずれかを入力して下さい。\n"); } } printf( " 年号を入力して下さい。 " ); scanf( "%s", &yn_buf ); printf("\n"); }

  • atoi

    123と表示されたいんだけど、コンパイルエラーです。 この場合、atoiの引数して、str[3]の文字を入れる変数をもう1つ用意するしかないですか? キャストでうまくできる方法があったら教えてください。 strという変数の"abc123"という文字は変化させたくないんです。 #include <iostream.h> main(){  int i;  char str[] = "abc123";  i = atoi(str[3]);  printf("%d\n", i); }

  • strncpy後のatoiがおかしい

    こんにちは。 C++をVS2005でやっています。 atoi関数を使っているんですが、10個の配列strにstrncpyをやると値がおかしくなります。 10個目に'\0'を代入させてやってみても駄目でした。 以下にソースを載せます。 #include <string.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> int main( void ) { char string[256]; char str[10]; char *moji = "12345464"; int l; // using template versions of strcpy_s and strcat_s: strcpy(string, "6877897898"); strcat(string, "strcpy_s"); strcat(string, "and"); // of course we can supply the size explicitly if we want to: strcat(string, "strcat_s!"); strncpy(str, string, 10); l = strtol(str,NULL,10);// 値が違う 6877897898にならない printf("str = %d\n", l); l = atoi(moji); printf("moji = %d\n", l); l = atoi(string);// 値が違う printf("string = %d\n", l); printf("String = %s\n", string); getchar(); return 0; } 表示結果 str = 2147483647 moji = 12345464 string = 2147483647 String = 6877897898 mojiは正常に動作しますから、ナル文字が原因なのかと思ってしまいますが。原因がいまいち分かりません。 よろしくお願いします。

  • C言語 コンパイルできるが実行するとエラー

    非負の数字を入力してもらい表示させ、数字以外を入力した場合は終了するプログラムです↓(VC使用) ━━━━━━━━━━━━━━━━━━━━━━━━━━ #include "stdafx.h" #include <stdio.h> #include <stdlib.h> /* atoi使用 */ #include <ctype.h> /* isdigit使用 */ int main(void) {   char cx;   int nx;   printf("非負の整数を入力してください。:");   scanf("%s", cx);   if(isdigit(cx) == 0){     printf("数字を入れてください。\n");   }else{     nx = atoi((const char*) cx);   }   printf("%d", nx);   return 0; } ━━━━━━━━━━━━━━━━━━━━━━━━━━ 実行し文字を入力するとDebug Error!と小窓がでます。どこがおかしいのでしょうか?

  • カレンダープログラム、曜日の出力について。

    西暦、月、日(1900年1月1日以降対象)を入力して、曜日を求めるプログラムを考えています。 過去の質問なども参考にしたんですが、プログラムがうまく動かず困っています。 プログラム中コメントの日数を変える辺りに不備があるかと思い色々試しましたが、曜日がずれてしまいますし開始1900年1月1日も月曜なのに水曜と出力されます。 forやifの条件の記述がおかしいのでしょうか? なにか根本的な所が欠如しているのでしょうか、回答をよろしくお願いします。 始めのgetsの所はあえてです。 #include<stdio.h> #include<stdlib.h> int main(void) { char ss[9],cop1[5],cop2[3],cop3[3]; int year,manth,day,aa,cc,i,f; gets(ss); ss[8] = 0; cop1[4] = 0; cop2[2] = 0; cop3[2] = 0; for(i = 0;i <= 3;i++){ cop1[i] = ss[i]; } for(i = 0;i <= 1;i++){ cop2[i] = ss[i+4]; } for(i = 0;i <= 1;i++){ cop3[i] = ss[i+6]; } year = atoi(cop1); manth = atoi(cop2); day = atoi(cop3); printf("%d年%d月%d日",year,manth,day); for(f = 1900; f <= year; f++){  //閏年か平年で日数を変える if((year%4) == 0 && year != 1900){ aa += 366; } else{ aa += 365; } } if((year%4) == 0 && manth >= 3 ){//閏年かつ3月以降日数+1 aa += 1; } switch(manth){ case 1: cc = (aa + day)%7; break; case 2: cc = (aa + 31 +day)%7; break; case 3: cc = (aa + 59 +day)%7; break; case 4: cc = (aa + 90 +day)%7; break; case 5: cc = (aa + 120 +day)%7; break; case 6: cc = (aa + 151 +day)%7; break; case 7: cc = (aa + 181 +day)%7; break; case 8: cc = (aa + 212 +day)%7; break; case 9: cc = (aa + 243 +day)%7; break; case 10: cc = (aa + 273 +day)%7; break; case 11: cc = (aa + 304 +day)%7; break; case 12: cc = (aa + 334 +day)%7; break; } switch(cc){ case 1: printf(" (月)"); break; case 2: printf(" (火)"); break; case 3: printf(" (水)"); break; case 4: printf(" (木)"); break; case 5: printf(" (金)"); break; case 6: printf(" (土)"); break; case 0: printf(" (日)"); break; } return 0; }

  • scanfについて

    いつもお世話になっております ------------------------ #include<stdio.h> #include<stdlib.h> int main(void){ int age , kyoku; char buff[16]; while(age){ printf("0で終了/年齢を入力して下さい"); scanf("%d",buff); age = atoi(buff); if( age == 0 ){ printf("終了します\n"); break; } ・ ・ ・ ------------------------ 上記のプログラムは、 年齢を入力させて もしも0だったら処理を終了させるという プログラムです。(つもり) scanfで入力された値を いったんバッファに蓄えて atoiで整数に変換させているつもりなんですが 例えば、「20」と入力しても ageの値が0、buffの値がエラーになってしまいます どこがおかしいのが ご教授して頂けたら幸いです。

  • Borland C CSVファイル読み込み

    CSVファイルを読み込み、読み込んだ値で計算を行うプログラムを作っています。 ・環境はWindows VISTA UltimateでBorland C++ Compiler 5.5  ・CSVファイルのデータの形式は 1,4532 4,2131 6,4301 . . ・データ数は決まっていて今のところ全部で12個 そして以下のようにCSVの読み込みプログラムを試しに組んだ所、実行時エラーがでました。 #include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { int AN[11][1]; int i=0,j=0,c=0; char buff[1024], *tp; FILE *fp; /*配列初期化*/ for(i=0;i<11;i++) { for(j=0;j<2;j++) { AN[i][j]=0; } } fp=fopen("test1.csv","r"); if(fp==NULL) { /* オープン失敗 */ printf("ファイルがオープンできません\n"); exit(1); /* 強制終了 */ } while( fgets(buff, 1024 , fp) != NULL ) { tp=strtok(buff , ","); if (tp !=NULL) {AN[i][j] = atoi(tp);} printf("%d\n",AN[i][j]); tp = strtok(NULL , ","); if (tp !=NULL) {AN[i][j+1] = atoi(tp);} printf("%d\n",AN[i][j+1]); i++; } fclose(fp); } 実行するとファイルクローズの後、問題が発生したためプログラムを終了しましたと出ます。"AN[i][j]=atoi(tp)"の配列部分を単純に変数にするとこのようなエラーは出ないのですが。 なぜエラーが出るのか、どなたかご教授願います。

  • 次のソースの使い方(strtok()関数)

     次のソースプログラムについてです。 (“□”は、タブを表します) ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆ #include <stdio.h> #include <stdlib.h> /* EXIT_SUCCESS */ #include <string.h> /* strtok() */ int main(void) { □int i, n; □char str[10], *token1, *token2; □scanf("%d", &n); □for (i=0; i<n; i++) { □□scanf("%s", str); □□token1 = strtok(str, ","); □□token2 = strtok(NULL, ","); □□printf("hello = %s , world = %s\n" ,token1 ,token2); □} □return EXIT_SUCCESS; } ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆  これを実行すると、入力待ち画面になって、何を入力すれば何が得られるのか、てんで分からないのですが、どなたか、このプログラムの使い方と意味について、解説をお願いします。  ちなみに、“1”を入力した後、[Ctrl]+[c]で抜けると、結果は、 ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆ hello = ヒヒz@俳・, world = (null)◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆ となりました。

  • strtokでの空文字への置き換え

    大したことじゃないと言えばそうかもしれませんが、ちょっと氣になるんで質問させてください。 C言語でstrtokという函數ありますよね。 第1引數の文字列を、第2引數の文字列を構成する文字で區切る。 第2引數の文字を見つけたら、それを空文字('¥0')に置き換える。 字句の最初の文字へのポインタを返す。 このようなものだと理解しています。 次のプログラムを實行してみました。 #include <stdio.h> #include <string.h> int main(void) { char string[]="XYZ1231ABC"; int i; printf("%s\n", string); putchar('\n'); printf("%s\n", strtok(string, "1A")); printf("%s\n", strtok(NULL, "1A")); printf("%s\n", strtok(NULL, "1A")); printf("%s\n", strtok(NULL, "1A")); putchar('\n'); for(i=0; i<=10; i++) printf("string[%d]=%c\n", i, string[i]); return 0; } 結果 XYZ1231ABC XYZ 23 BC (null) string[0]=X string[1]=Y string[2]=Z string[3]= string[4]=2 string[5]=3 string[6]= string[7]=A string[8]=B string[9]=C string[10]= 私が思うには、string[7]は空文字に置き換わってしまうはずだと思うんですが、 結果は'A'のままです。 ここが '¥0'に置き換わるかどうかは しょり系によって異なるのでしょうか。