C言語でファイルの名前を入力し、そのファイルの名前があればそのファイルを「.」の前までをモードとして開きたいのですが、モードが存在しない場合でもエラーにならずに「開けません」と表示できるプログラムを作るにはどうしたらよいですか?
[プログラム]
#include<stdio.h>
int main(void)
{
FILE *fp; int i; char filename[256]; char mode[20] = {0}; printf("ファイルネームを入力してください。\n"); scanf("%s",filename); for(i = 0;i < 20;i++) { if(filename[i] == '.') { break; } else mode[i] = filename[i]; } printf("%s",mode); fp = fopen(filename,mode); if(fp == NULL) printf("%sを開けません。\n",filename); else { printf("%sを開きました。\n",filename); fclose(fp); } return 0;
}
[エラー]
Line:98
Expression:(Invalid file open mode",0)
For information on how your program can cause an assertion failure, see the Visual C++ documentation on asserts.
(Press Retry to debug the application)
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/01/22 22:22 編集
2016/01/22 22:23
回答8件
0
全体的に作り直してみました。check_mode()
がモードの文字列が正しいか確認する関数です。C11の仕様書に厳密に従っていますので、C11に対応してないVC++ではうまく行かない可能性があります。VC++に合わせるなら、'x'は削除して、'b'との排他で't'を追加するのがいいと思います。VC++ではほかにもたくさん指定できるようですので、そこら辺はMSDNを参考に追加していってください。
参考: MSDN: fopen、_wfopen
C
1#include <stdio.h> 2#include <stdbool.h> 3#include <string.h> 4#include <errno.h> 5 6#define FILE_NAME_MAX_LENGTH 255 7#define FILE_NAME_BUFFER_SIZE (FILE_NAME_MAX_LENGTH + 1) 8#define MODE_SEPARATOR ':' 9 10/* 11`fopen()`のモードについてC11で規定されているのは次の文字列のみである。 12 r w wx a 13 rb wb wbx ab 14 r+ w+ w+x a+ 15 r+b rb+ w+b wb+ w+bx wb+x a+b ab+ 16`fopen_s()`では上記に加え次が可能である。 17 uw uwx ua 18 uwb uwbx uab 19 uw+ uw+x ua+ 20 uwb+ uw+b uwb+x uw+bx uab+ ua+b 21上記以外は動作が**未定義**となっている。まとめると次のようになる。 221. 最初はr, w, a のどれかである 232. 次は +, b であり、順番はどちらで良い 243. 最後は x であり、w の時のみ許される 254. `fopen_s()`では上記に加え、w, a のときに u が最初にあっても良い 26*/ 27bool check_mode(const char *mode) 28{ 29 char rwa = *mode++; 30 if (rwa != 'r' && rwa != 'w' && rwa != 'a') return false; 31 bool plu = false; 32 bool bin = false; 33 bool exi = false; 34 for (; *mode != '\0'; mode++) { 35 switch (*mode) { 36 case '+': 37 if (plu || exi) return false; 38 plu = true; 39 break; 40 case 'b': 41 if (bin || exi) return false; 42 bin = true; 43 break; 44 case 'x': 45 if (rwa != 'w' || exi) return false; 46 exi = true; 47 break; 48 default: 49 return false; 50 } 51 } 52 return true; 53} 54 55bool check_mode_s(const char *mode) 56{ 57 if (*mode == 'u') { 58 mode++; 59 if (*mode != 'w' && *mode != 'a') return false; 60 } 61 return check_mode(mode); 62} 63 64int main(void) 65{ 66 char input_str[FILE_NAME_BUFFER_SIZE] = { 0 }; 67 68 // ファイル名の取得 69 bool get_continue = true; 70 printf(u8"モード付ファイル名(モード%cファイル名)を入力してください。 " 71 u8"(最大%d文字)\n", MODE_SEPARATOR, FILE_NAME_MAX_LENGTH); 72 for (int i = 0; get_continue && i < FILE_NAME_MAX_LENGTH + 1; i++) { 73 int c = getchar(); 74 switch (c) { 75 case EOF: 76 case '\n': 77 case '\r': 78 get_continue = false; 79 break; 80 default: 81 input_str[i] = c; 82 } 83 } 84 if (get_continue) { 85 fprintf(stderr, 86 u8"最大文字数がこえています。" 87 u8"%d文字以上は指定できません。\n", 88 FILE_NAME_MAX_LENGTH); 89 return 2; 90 } 91 92 // モード分離文字を探索 93 char *sep_point = strchr(input_str, MODE_SEPARATOR); 94 if (sep_point == NULL) { 95 fprintf(stderr, 96 u8"モードの指定がありません。\n"); 97 return 3; 98 } 99 100 // ファイル名 101 char *file_name = sep_point + 1; 102 if (strnlen(file_name, 1) == 0) { 103 fprintf(stderr, 104 u8"ファイル名がありません。\n"); 105 return 4; 106 } 107 108 // モード部分の長さを求める 109 ssize_t mode_length; 110 mode_length = sep_point - input_str; 111 // 1から4文字で無ければならない 112 if (mode_length < 1 || mode_length > 4) { 113 fprintf(stderr, 114 u8"モード部分が1から4文字でありません。\n"); 115 return 5; 116 } 117 char mode[5] = { 0 }; 118 strncpy(mode, input_str, mode_length); 119 120 // モードが正しいかチェック 121 if (!check_mode(mode)) { 122 fprintf(stderr, 123 u8"%s はモードとして不正です。\n", 124 mode); 125 return 6; 126 } 127 128 // ファイルオープン 129 FILE *file; 130 file = fopen(file_name, mode); 131 if (file == NULL) { 132 int eno = errno; 133 fprintf(stderr, 134 u8"%s ファイルを開くのに失敗しました。\n", 135 file_name); 136 fprintf(stderr, 137 u8"原因: %s\n", strerror(eno)); 138 return 1; 139 } 140 printf(u8"%s ファイルを開く事ができました。\n", file_name); 141 fclose(file); 142 143 return 0; 144}
check_mode_s()
はfopen_s()
のモード指定用ですがC11準拠の実装が存在してなさそうなので誰得です。UTF-8文字列を使っているので、少なくともVC++2015以上じゃ無いとコンパイルができないと思います。Windowsで動かすときは、コマンドプロンプトのエンコードをUTF-8にしてください。その他、scanf("%s", ...)
だと、空白が入ったファイル名を受け取れないし、バッファオーバーフローするし、ということで一文字ずつ取るようにしています。fgets()
でも良かったと今気付きました。モードを分離する文字は"."だと拡張子の"."とわかりにくいので":"にしています。Windowsだとドライブ文字との区切り使われるので、あまりよろしくないことに、これまた今気付きました。
投稿2016/01/23 07:16
総合スコア21733
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
こんな方法もありか・・・
C
1bool error= true; 2if( mode[0] == 'a' || mode[0] == 'r' || mode[0] == 'w' ){ 3 size_t len= strlen(mode); 4 switch(len){ 5 case 1: 6 error= false; 7 break; 8 case 2: 9 if( mode[1] == '+' || mode[1] == 'b' ){ 10 error= false; 11 } 12 break; 13 case 3: 14 if( mode[1] == '+' && mode[2] == 'b' ){ 15 error= false; 16 } 17 if( mode[1] == 'b' && mode[2] == '+' ){ 18 error= false; 19 } 20 break; 21 } 22} 23
短縮できそうな所もありますが、雰囲気が分かればいいかと・・・
投稿2016/01/23 03:38
編集2016/01/23 03:45総合スコア6851
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
こんにちは。
モードが存在しない場合でもエラーにならずに「開けません」と表示できるプログラムを作るにはどうしたらよいですか?
ちょっと面倒ですが、下記でできると思います。printf("%s",mode);
の次の行へ入れて下さい。
C
1if ((strcmp(mode, "r") 2 && (strcmp(mode, "w") 3 && (strcmp(mode, "a") 4 && (strcmp(mode, "r+") 5 && (strcmp(mode, "w+") 6 && (strcmp(mode, "a+") 7 && (strcmp(mode, "rb") 8 && (strcmp(mode, "wb") 9 && (strcmp(mode, "ab") 10 && (strcmp(mode, "r+b") 11 && (strcmp(mode, "w+b") 12 && (strcmp(mode, "a+b") 13 && (strcmp(mode, "rb+") 14 && (strcmp(mode, "wb+") 15 && (strcmp(mode, "ab+")) { 16 printf("%sを開けません。\n",filename); 17return 0; 18}
モード文字列についてはここを参考にしましたが、コンパイラによって異なりますのでお使いのコンパイラのリファレンスを確認下さい。
なお、この仕様の是非については他の回答者様の仰る通りと思います。
必要性が今ひとつ理解し辛い仕様です。
投稿2016/01/22 14:18
総合スコア23272
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/01/23 02:17
2016/01/23 04:07 編集
0
何をやろうとしているのかはよく判りませんが、ファイル名の一部をオープンモードとするアイディアは聞いたことがありませんし、無用な混乱を招くので別の方法を考えた方が良いと思います。
それとは別にアサーションが発生する問題ですが、C標準の仕様かどうかは定かではないのですが、VC++ではfopenの引数(ファイル名、モード)にNULLか空文字列を渡すと無効な引数ハンドラーが呼び出されます。
したがって、fopen関数を呼び出す前に引数のチェックをすれば問題を回避できます。
コード例
C
1if(filename[0] == '\0' || mode[0] == '\0') 2{ 3 printf("引数に誤りがあります\n"); 4 return 0; 5}
C標準関数で例外はないですよね。アサーションに修正しました。
投稿2016/01/22 07:24
編集2016/01/22 07:49総合スコア5938
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
例えば "r.c:\temp.txt" のような感じの入力を想定されているのでしょうか?
↓ 以下見落としにつき削除
まず、 mode の終端処理が必要です。おそらく "." を見つけた時点で、mode の街頭位置に '\0'を書き込まないと mode が文字列として終端されません。
↑ 削除ここまで 取り消し線があるとよいなぁ。
また、 filename から "." 以前を取り除く処理が必要です。
蛇足ですが、ファイル名の構成要素に "." が許されていますので、モードとファイルパスの区切りに "." を使うのは仕様的にあまり良い方法ではありません。これによって、モードの指定を必須にせざる負えなくなり、省略することはできなくなります。(省略時に、ファイル名の一部を誤認するため)
投稿2016/01/22 07:08
編集2016/01/22 07:32総合スコア915
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/01/22 07:17
2016/01/22 07:30
0
fopenについて勘違いしている様な気がします。
モードが存在しない場合でもエラーにならずに「開けません」と表示
いまいち意図が汲み取れません...。
検証していませんが、fopenのモードとして指定できるモード以外を引数として渡した場合、
エラーが出るのは当たり前の様な気がします。
fopenを実行する前にモードとして何が設定されているか判断してはじけば良いのではないでしょうか?
また、ソースからfilenameの先頭から拡張子までを
モードに設定していると読み解きましたが、何をしたいのでしょうか?
fopenは以下の様に使用します。
例)fopen(testfile.txt, "r")
投稿2016/01/22 07:03
総合スコア59
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/01/22 07:36
0
現状のコードでは、モードが正しい場合でも正常に動作しません。というのも、最初にモードの付いたfilename
をそのままfopen
に渡した場合、本来のファイル名でないものが頭に入ったままとなるので、存在しないファイル、もしくは意図しないファイルへのアクセスとなってしまいます。
…というより、根本的な問題として、ファイルを開いたまま他のプロセスへ渡すような状況でないなら、エンドユーザーからfopen
のモードを指定できる意味はほぼないと思います。テストだけなら書き換えてコンパイルを繰り返すほうが適切です。
投稿2016/01/22 07:01
総合スコア145121
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。