ファイルの利用 解答ページ | Programming Place Plus C言語編 第39章

トップページC言語編第39章

問題① 🔗

問題① fopen関数の第1引数を、絶対パスで指定して実行結果を確かめてみてください。


fopen関数の第1引数を絶対パス表記に変えるだけです。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE* fp = fopen("C:/Program/hello.txt", "w");
    if (fp == NULL) {
        fputs("ファイルオープンに失敗しました。\n", stderr);
        exit(EXIT_FAILURE);
    }

    if (fputs("Hello, World\n", fp) == EOF) {
        fputs("ファイルへの書き込みに失敗しました。\n", stderr);
        exit(EXIT_FAILURE);
    }

    if (fclose(fp) == EOF) {
        fputs("ファイルクローズに失敗しました。\n", stderr);
        exit(EXIT_FAILURE);
    }
}

実行結果(標準出力):

実行結果(hello.txt):

Hello, World

このサンプルプログラムは、Windows環境をターゲットにしたものです。 また、自分の使っている PC の環境によって、 先頭が「C:\」ではなく「D:\」などの他のアルファベットが割り当てられているなど、違いがあると思います。 自分の環境に合わせて変えてください。

macOS の場合、たとえば次のように書きます。

fp = fopen("/Users/myname/Desktop/Program/hello.txt, "w");


環境によりますが、指定したパス次第では、新しいファイルを作成することが許可されていない可能性もあります。そういうパスを指定した場合には、fopen関数はエラーになり、ヌルポインタを返すでしょう。

問題② 🔗

問題② 標準入力からファイル名を入力させ、そのファイルに対して “Hello, World” を出力するプログラムを作成してください。


ファイルパスの長さの限界は、FILENAME_MAX ですから、その大きさで char型配列を準備しておき、そこへ入力結果を格納します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    puts("出力先となるファイル名を入力してください。");
    char path[FILENAME_MAX];
    fgets(path, sizeof(path), stdin);

    // 末尾の改行文字を削除
    path[strlen(path)-1] = '\0';


    FILE* fp = fopen(path, "w");
    if (fp == NULL) {
        fputs("ファイルオープンに失敗しました。\n", stderr);
        exit(EXIT_FAILURE);
    }

    if (fputs("Hello, World\n", fp) == EOF) {
        fputs("ファイルへの書き込みに失敗しました。\n", stderr);
        exit(EXIT_FAILURE);
    }

    if (fclose(fp) == EOF) {
        fputs("ファイルクローズに失敗しました。\n", stderr);
        exit(EXIT_FAILURE);
    }
}

実行結果(標準出力):

実行結果(ファイル):

Hello, World

fgets関数を使って入力を受け取ると、末尾に改行文字が加わってしまいます。 これが好ましくなければ、改行文字を取り除く処理が必要です。 ここでのやり方はあまり良いものでもありませんが、目的は果たせます。

こういう処理はたびたび必要になって鬱陶しいので、関数化して自分の便利関数ヘッダに加えておくと良いでしょう。 たとえば、コードライブラリppp_stdio.c に含まれている ppp_get_line関数は一例です。

問題③ 🔗

問題③ 絶対パスと相対パスの使い分けは、実用上どんな違いを生むか考えてみてください。


絶対パス指定を使ったプログラムは、他のコンピュータに持っていくと、うまく動かなくなる可能性があります。自分のコンピュータでは、「C:\Program」のように Cドライブを使っていても、他のコンピュータは「D:\Program」のように他のドライブ名かもしれません。ですから一般的に、他人に配布する場合に絶対パスは使えません。

相対パス指定の場合には、そういった問題はありませんが、基準となるディレクトリが変われば、実際に表現しようとしているパスも変わります。たとえば、プログラムの実行ファイルがある場所をカレントディレクトリとすると、その実行ファイルをコピーして別の場所へ持っていくだけで、表現されるパスも変わります。

【上級】環境変数+相対パス表記の組み合わせを使うことで、コンピュータごとの差異を吸収することもあります。

問題④ 🔗

問題④ ファイルパスを文字列で与えると、拡張子部分を返す関数を作成してください。


拡張子は「.」に続けて数文字の英数字が並びます。そこで、文字列を末尾側から逆順にたどって、‘.’ を探し、発見できれば、そのときの(‘.’ の)メモリアドレスを返せば良さそうです。

関数の引数と戻り値の設計が少し問題です。ファイルパスは、const char* の引数で受け取ればよいですが、結果をどう返すべきでしょう。

1つの方法は次の解答例のように戻り値で返すことです。この場合は、path の拡張子部分を指すポインタを返すことになります。

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

const char* get_extension(const char* path);

int main(void)
{
    const char* ext = get_extension("test.bin");
    if (ext != NULL) {
        puts(ext);
    }

    ext = get_extension("program/test.c");
    if (ext != NULL) {
        puts(ext);
    }

    ext = get_extension("program/test");
    if (ext != NULL) {
        puts(ext);
    }
}

const char* get_extension(const char* path)
{
    for (int i = strlen(path) - 1; i >= 0; --i) {
        if (path[i] == '.') {
            return &path[i];
        }
    }

    return NULL;
}

実行結果:

.bin
.c

見つからなかったときは、ヌルポインタを返すことにしました。

これと同じ操作は、標準ライブラリ関数の strrchr関数で行えます。

ほかにも関数の仕様は考えられますが、C言語の場合、どうしても動的なメモリ確保が絡んできたり、呼び出し側で適切な大きさの配列を用意しなければならなかったりして、あまり効率的かつ便利にはならなさそうです。



参考リンク 🔗


更新履歴 🔗

≪さらに古い更新履歴≫

 第48章の練習問題③を移動してきて、練習問題④とした。

 「NULL」よりも「ヌルポインタ」が適切な箇所について、「ヌルポインタ」に修正。

 全面的に文章を見直し、修正を行った。

 flose関数の戻り値もチェックするようにした。

 OS X に対応。

 エラー発生時には、exit関数で失敗終了させるようにサンプルプログラムを修正。

 新規作成。



第39章のメインページへ

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る