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

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

ただいまの
回答率

90.51%

  • C

    4514questions

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

  • C++

    4418questions

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

C言語 ファイルオープン

解決済

回答 8

投稿

  • 評価
  • クリップ 0
  • VIEW 2,165

bobslay

score 7

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)

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • otn

    2016/01/22 21:50

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

    キャンセル

  • greengreen

    2016/01/23 07:19 編集

    文字列は最後に必ずゼロを入れるのを忘れないようにしないとダメですよ。このプログラムですと、25行目に
    mode[i] = 0;
    を挿入してください。

    余談ですが、自分はMS-DOSのMicrosoft CからC言語を始めたクチですので、こういうC言語のプログラムは大変懐かしいですね!
    色々フリーソフトも作りました(*^^*)

    キャンセル

  • greengreen

    2016/01/23 07:23

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

    キャンセル

回答 8

checkベストアンサー

0

こんにちは。

モードが存在しない場合でもエラーにならずに「開けません」と表示できるプログラムを作るにはどうしたらよいですか? 

ちょっと面倒ですが、下記でできると思います。printf("%s",mode);の次の行へ入れて下さい。

if ((strcmp(mode, "r")
 && (strcmp(mode, "w")
 && (strcmp(mode, "a")
 && (strcmp(mode, "r+")
 && (strcmp(mode, "w+")
 && (strcmp(mode, "a+")
 && (strcmp(mode, "rb")
 && (strcmp(mode, "wb")
 && (strcmp(mode, "ab")
 && (strcmp(mode, "r+b")
 && (strcmp(mode, "w+b")
 && (strcmp(mode, "a+b")
 && (strcmp(mode, "rb+")
 && (strcmp(mode, "wb+")
 && (strcmp(mode, "ab+")) {
  printf("%sを開けません。\n",filename);
return 0;
}


モード文字列についてはここを参考にしましたが、コンパイラによって異なりますのでお使いのコンパイラのリファレンスを確認下さい。


なお、この仕様の是非については他の回答者様の仰る通りと思います。
必要性が今ひとつ理解し辛い仕様です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/23 11:17

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

    キャンセル

  • 2016/01/23 13:06 編集

    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へ戻しておくことをお勧めします。

    キャンセル

0

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/22 16:36

    > >モードが存在しない場合でもエラーにならずに「開けません」と表示
    > いまいち意図が汲み取れません...。
    > 検証していませんが、fopenのモードとして指定できるモード以外を引数として渡した場合、
    > エラーが出るのは当たり前の様な気がします。

    質問者さんの言うエラーとは、例外が発生してプロセスが中断してしまったことですね。
    対処方法についてはその通りだと思います。

    キャンセル

0

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

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

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/22 16:17

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

    キャンセル

  • 2016/01/22 16:30

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

    キャンセル

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

何をやろうとしているのかはよく判りませんが、ファイル名の一部をオープンモードとするアイディアは聞いたことがありませんし、無用な混乱を招くので別の方法を考えた方が良いと思います。

それとは別にアサーションが発生する問題ですが、C標準の仕様かどうかは定かではないのですが、VC++ではfopenの引数(ファイル名、モード)にNULLか空文字列を渡すと無効な引数ハンドラーが呼び出されます。
したがって、fopen関数を呼び出す前に引数のチェックをすれば問題を回避できます。

コード例

if(filename[0] == '\0' || mode[0] == '\0')
{
    printf("引数に誤りがあります\n");
    return 0;
}

C標準関数で例外はないですよね。アサーションに修正しました。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

こんな方法もありか・・・

bool error= true;
if( mode[0] == 'a' || mode[0] == 'r' || mode[0] == 'w' ){
    size_t len= strlen(mode);
    switch(len){
    case 1:
        error= false;
        break;
    case 2:
        if( mode[1] == '+' ||  mode[1] == 'b' ){
            error= false;
        }
        break;
    case 3:
        if( mode[1] == '+' && mode[2] == 'b' ){
            error= false;
        }
        if( mode[1] == 'b' && mode[2] == '+' ){
            error= false;
        }
        break;
    }
}


短縮できそうな所もありますが、雰囲気が分かればいいかと・・・

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

全体的に作り直してみました。check_mode()がモードの文字列が正しいか確認する関数です。C11の仕様書に厳密に従っていますので、C11に対応してないVC++ではうまく行かない可能性があります。VC++に合わせるなら、'x'は削除して、'b'との排他で't'を追加するのがいいと思います。VC++ではほかにもたくさん指定できるようですので、そこら辺はMSDNを参考に追加していってください。
参考: MSDN: fopen、_wfopen

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>

#define FILE_NAME_MAX_LENGTH 255
#define FILE_NAME_BUFFER_SIZE (FILE_NAME_MAX_LENGTH + 1)
#define MODE_SEPARATOR ':'

/*
`fopen()`のモードについてC11で規定されているのは次の文字列のみである。
  r w wx a
  rb wb wbx ab
  r+ w+ w+x a+
  r+b rb+ w+b wb+ w+bx wb+x a+b ab+
`fopen_s()`では上記に加え次が可能である。
  uw uwx ua
  uwb uwbx uab
  uw+ uw+x ua+
  uwb+ uw+b uwb+x uw+bx uab+ ua+b
上記以外は動作が**未定義**となっている。まとめると次のようになる。
1. 最初はr, w, a のどれかである
2. 次は +, b であり、順番はどちらで良い
3. 最後は x であり、w の時のみ許される
4. `fopen_s()`では上記に加え、w, a のときに u が最初にあっても良い
*/
bool check_mode(const char *mode)
{
    char rwa = *mode++;
    if (rwa != 'r' && rwa != 'w' && rwa != 'a') return false;
    bool plu = false;
    bool bin = false;
    bool exi = false;
    for (; *mode != '\0'; mode++) {
        switch (*mode) {
            case '+':
                if (plu || exi) return false;
                plu = true;
                break;
            case 'b':
                if (bin || exi) return false;
                bin = true;
                break;
            case 'x':
                if (rwa != 'w' || exi) return false;
                exi = true;
                break;
            default:
                return false;
        }
    }
    return true;
}

bool check_mode_s(const char *mode)
{
    if (*mode == 'u') {
        mode++;
        if (*mode != 'w' && *mode != 'a') return false;
    }
    return check_mode(mode);
}

int main(void)
{
    char input_str[FILE_NAME_BUFFER_SIZE] = { 0 };

    // ファイル名の取得
    bool get_continue = true;
    printf(u8"モード付ファイル名(モード%cファイル名)を入力してください。 "
        u8"(最大%d文字)\n", MODE_SEPARATOR, FILE_NAME_MAX_LENGTH);
    for (int i = 0; get_continue && i < FILE_NAME_MAX_LENGTH + 1; i++) {
        int c = getchar();
        switch (c) {
            case EOF:
            case '\n':
            case '\r':
                get_continue = false;
                break;
            default:
                input_str[i] = c;
        }
    }
    if (get_continue) {
        fprintf(stderr,
            u8"最大文字数がこえています。"
            u8"%d文字以上は指定できません。\n",
            FILE_NAME_MAX_LENGTH);
        return 2;
    }

    // モード分離文字を探索
    char *sep_point = strchr(input_str, MODE_SEPARATOR);
    if (sep_point == NULL) {
        fprintf(stderr,
            u8"モードの指定がありません。\n");
        return 3;
    }

    // ファイル名
    char *file_name = sep_point + 1;
    if (strnlen(file_name, 1) == 0) {
        fprintf(stderr,
            u8"ファイル名がありません。\n");
        return 4;
    }

    // モード部分の長さを求める
    ssize_t mode_length;
    mode_length = sep_point - input_str;
    // 1から4文字で無ければならない
    if (mode_length < 1 || mode_length > 4) {
        fprintf(stderr,
            u8"モード部分が1から4文字でありません。\n");
        return 5;
    }
    char mode[5] = { 0 };
    strncpy(mode, input_str, mode_length);

    // モードが正しいかチェック
    if (!check_mode(mode)) {
        fprintf(stderr,
            u8"%s はモードとして不正です。\n",
            mode);
        return 6;
    }

    // ファイルオープン
    FILE *file;
    file = fopen(file_name, mode);
    if (file == NULL) {
        int eno = errno;
        fprintf(stderr,
            u8"%s ファイルを開くのに失敗しました。\n",
            file_name);
        fprintf(stderr,
            u8"原因: %s\n", strerror(eno));
        return 1;
    }
    printf(u8"%s ファイルを開く事ができました。\n", file_name);
    fclose(file);

    return 0;
}


check_mode_s()fopen_s()のモード指定用ですがC11準拠の実装が存在してなさそうなので誰得です。UTF-8文字列を使っているので、少なくともVC++2015以上じゃ無いとコンパイルができないと思います。Windowsで動かすときは、コマンドプロンプトのエンコードをUTF-8にしてください。その他、scanf("%s", ...)だと、空白が入ったファイル名を受け取れないし、バッファオーバーフローするし、ということで一文字ずつ取るようにしています。fgets()でも良かったと今気付きました。モードを分離する文字は"."だと拡張子の"."とわかりにくいので":"にしています。Windowsだとドライブ文字との区切り使われるので、あまりよろしくないことに、これまた今気付きました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • C

    4514questions

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

  • C++

    4418questions

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