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

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

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

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

8回答

5695閲覧

C言語 ファイルオープン

bobslay

総合スコア32

C

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2016/01/22 06:40

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ページで確認できます。

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

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

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

otn

2016/01/22 12:50

コード部分を、```C と ``` という行で囲んでください。
greengreen

2016/01/22 22:22 編集

文字列は最後に必ずゼロを入れるのを忘れないようにしないとダメですよ。このプログラムですと、25行目に mode[i] = 0; を挿入してください。 余談ですが、自分はMS-DOSのMicrosoft CからC言語を始めたクチですので、こういうC言語のプログラムは大変懐かしいですね! 色々フリーソフトも作りました(*^^*)
greengreen

2016/01/22 22:23

char mode[20] = {0};でゼロクリア既にされてましたね。失礼しました^^;
guest

回答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

raccy

総合スコア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
cateye

総合スコア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

Chironian

総合スコア23272

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

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

bobslay

2016/01/23 02:17

これ以外ないですかね。 これしか思いつかないので投稿したのですが‥‥
Chironian

2016/01/23 04:07 編集

VC++限定であれば、_set_invalid_parameter_handler()を使う手はありそうです。 https://msdn.microsoft.com/ja-jp/library/a9yf33zb.aspx このページのサンプルのように記述し、myInvalidParameterHandler()で"開けません"と表示後、_exit()で終了させることが考えられます。 https://msdn.microsoft.com/en-us/library/6wdz5232.aspx なお、ハンドラーを置き換えたままにすると、思わぬ時に"開けません"メッセージを出して通常終了するので、fopenの直後にoldHandlerへ戻しておくことをお勧めします。
guest

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
catsforepaw

総合スコア5938

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

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

0

やりたいことがよくわからないのですが、例えばファイル名として r.aaa.txt を渡せば
fopen("aaa.txt","r")
としてオープンするということでしょうか。
そうであれば、まず入力されたファイル名を最初の . で "r" と "aaa.txt" に分割し、
それで fopen を呼ぶ必要があります。

投稿2016/01/22 07:11

yoshi777

総合スコア674

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

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

0

例えば "r.c:\temp.txt" のような感じの入力を想定されているのでしょうか?

↓ 以下見落としにつき削除

まず、 mode の終端処理が必要です。おそらく "." を見つけた時点で、mode の街頭位置に '\0'を書き込まないと mode が文字列として終端されません。

↑ 削除ここまで 取り消し線があるとよいなぁ。

また、 filename から "." 以前を取り除く処理が必要です。

蛇足ですが、ファイル名の構成要素に "." が許されていますので、モードとファイルパスの区切りに "." を使うのは仕様的にあまり良い方法ではありません。これによって、モードの指定を必須にせざる負えなくなり、省略することはできなくなります。(省略時に、ファイル名の一部を誤認するため)

投稿2016/01/22 07:08

編集2016/01/22 07:32
T.Kanno

総合スコア915

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

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

maisumakun

2016/01/22 07:17

modeの終端処理についてですが、宣言時に0クリアされているので、.の手前までコピーした段階で適切に処理されていると思います。
T.Kanno

2016/01/22 07:30

おっと、失礼、見落としておりました。
guest

0

fopenについて勘違いしている様な気がします。

モードが存在しない場合でもエラーにならずに「開けません」と表示

いまいち意図が汲み取れません...。
検証していませんが、fopenのモードとして指定できるモード以外を引数として渡した場合、
エラーが出るのは当たり前の様な気がします。
fopenを実行する前にモードとして何が設定されているか判断してはじけば良いのではないでしょうか?

また、ソースからfilenameの先頭から拡張子までを
モードに設定していると読み解きましたが、何をしたいのでしょうか?
fopenは以下の様に使用します。
例)fopen(testfile.txt, "r")

投稿2016/01/22 07:03

u.-_-.u

総合スコア59

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

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

catsforepaw

2016/01/22 07:36

> >モードが存在しない場合でもエラーにならずに「開けません」と表示 > いまいち意図が汲み取れません...。 > 検証していませんが、fopenのモードとして指定できるモード以外を引数として渡した場合、 > エラーが出るのは当たり前の様な気がします。 質問者さんの言うエラーとは、例外が発生してプロセスが中断してしまったことですね。 対処方法についてはその通りだと思います。
guest

0

現状のコードでは、モードが正しい場合でも正常に動作しません。というのも、最初にモードの付いたfilenameをそのままfopenに渡した場合、本来のファイル名でないものが頭に入ったままとなるので、存在しないファイル、もしくは意図しないファイルへのアクセスとなってしまいます。

…というより、根本的な問題として、ファイルを開いたまま他のプロセスへ渡すような状況でないなら、エンドユーザーからfopenのモードを指定できる意味はほぼないと思います。テストだけなら書き換えてコンパイルを繰り返すほうが適切です。

投稿2016/01/22 07:01

maisumakun

総合スコア145123

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問