1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C言語で作る自作cpコマンド

Last updated at Posted at 2021-01-21

目的

C言語の学習のために、自作でlinuxのcpコマンド作ってみました。
とはいえ、本実装ではオプションないし、エラー処理等もなしなので、雑な作りになっています。
ただ、案外コピーするくらいなら簡単に実装できたので、他にもファイル処理等オリジナルコマンドが作れるようになったらいいなと思いました。

僕と同様にC言語学習中で何作っていいかわからない人向けです。
学習する上で、初めから作りたいものがあれば話は早いですが、そういうわけではない場合もあったりすると思います。
そんな時はすでにあるものを真似して作ってみるが一番ですよね。

メイン実装1(バグあり)

@fujitanozomu @SaitoAtsushi
お二方にご指摘いただき、下記プログラムだと、コメントでおっしゃってる通り、
ファイル中に0x00のバイト値を含むファイルは同一の内容にコピーされないです。
@fujitanozomu さん、 @SaitoAtsushi さん、ありがとうございます。
この下に、修正版を残しておきます。
ご指摘あれば、コメントください

copy.c
#include <stdio.h>

int main(int argc, char *argv[]) {
  int i;
  char ch[1000];
  FILE *file, *new_file; 

  if (argc < 3) {
    perror(argv[0]);
    return 1;
  }

  file = fopen(argv[1], "r");
  if (!file) {
    return 1;
  }
  new_file = fopen(argv[2], "w");
  if (!new_file) {
    return 1;
  }

  while ((fgets(ch, sizeof(ch), file)) != NULL) {
    if (fputs(ch, new_file) < 0) return 1;
  }

  fclose(file);
  fclose(new_file);

  return 0;
}

メイン実装2(修正版)

fgets => fgetc
fputs => fputc

copy.c
#include <stdio.h>

int main(int argc, char *argv[]) {
  int i, c;
  FILE *file, *new_file; 

  if (argc < 3) {
    perror(argv[0]);
    return 1;
  }

  file = fopen(argv[1], "r");
  if (!file) {
    return 1;
  }
  new_file = fopen(argv[2], "w");
  if (!new_file) {
    return 1;
  }

  while ((c = fgetc(file)) != EOF) {
    if (fputc(c, new_file) == EOF) return 1;
  }

  fclose(file);
  fclose(new_file);

  return 0;
}

メイン実装3(改善版)

@fujitanozomu さんより、よりパフォーマンスがいいコードの例を教えていただきました。
ありがとうございます。

copy.c
#include <stdio.h>

int main(int argc, char *argv[]) {
  FILE *file, *new_file; 

  if (argc < 3) {
    perror(argv[0]);
    return 1;
  }

  file = fopen(argv[1], "rb");
  if (!file) {
    return 1;
  }
  new_file = fopen(argv[2], "wb");
  if (!new_file) {
    return 1;
  }

  char buf[4096];
  size_t size;
  while ((size = fread(buf, sizeof(char), sizeof(buf), file)) > 0) {
    if (fwrite(buf, sizeof(char), size, new_file) <= 0) return 1;
  }

  fclose(file);
  fclose(new_file);
  return 0;
}

軽く解説

詳細説明とは多少異なります。自分なりの理解のもとでなるべくわかりやすく記述しています。
間違っている場合はご指摘ください。

fopen

  • 第1引数 = ファイルパス
  • 第2引数 = mode = ストリームの性質(書き込み専用・読み込み専用etc)
  • 返り値 = オープンしたストリームを制御するFILEへのポインタ。失敗した場合NULL

指定された読み込みファイル(コピー元)のオープン、
書き込みするためのファイル(コピー先)のオープンを行う。

fgets

  • 第1引数 = バッファ変数(配列)
  • 第2引数 = バッファのサイズ
  • 第3引数 = ストリーム(ファイル)
  • 返り値 = バッファ。失敗した場合・最後の行まで読み込んだ場合NULL

指定したストリーム(ファイル)から1行分の文字列を読み取り、バッファへ格納

fputs

  • 第1引数 = 任意の文字列()
  • 第2引数 = ストリーム(書き込み先ファイル)
  • 返り値 = 成功は0以上の正の値、失敗はEOF

任意の文字列を指定したストリームに書き込む
今回んプログラムの場合、バッファに格納した文字列を書き込むようにしている。

fclose

  • 第1引数 = ストリーム(open下もファイル)
  • 返り値 = 成功は0、失敗はEOF

オープンしたファイルを閉じる

----- 以下修正版で利用 -----

fgetc

  • 第1引数 = ストリーム(ファイル)
  • 返り値 = ストリームから1文字読み込んで、その文字をint型として返す。失敗した場合・最後の行まで読み込んだ場合EOF

fputc

  • 第1引数 = 書き込みたい文字(int)(fgetcで取得したバイトをそのまま扱える)
  • 第2引数 = ストリーム(ファイル)
  • 返り値 = 書き込んだ文字。失敗した場合EOF
1
0
10

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?