[解決済み】正気で、安全で、効率的な方法でファイルをコピーする。
質問
ファイル(バイナリまたはテキスト)をコピーする良い方法を探しています。私はいくつかのサンプルを書き、誰もが動作します。しかし、私はベテランのプログラマーの意見を聞きたいです。
良い例がなく、C++で動作する方法を探しています。
ANSI-C-WAY
#include <iostream>
#include <cstdio> // fopen, fclose, fread, fwrite, BUFSIZ
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
// BUFSIZE default is 8192 bytes
// BUFSIZE of 1 means one chareter at time
// good values should fit to blocksize, like 1024 or 4096
// higher values reduce number of system calls
// size_t BUFFER_SIZE = 4096;
char buf[BUFSIZ];
size_t size;
FILE* source = fopen("from.ogv", "rb");
FILE* dest = fopen("to.ogv", "wb");
// clean and more secure
// feof(FILE* stream) returns non-zero if the end of file indicator for stream is set
while (size = fread(buf, 1, BUFSIZ, source)) {
fwrite(buf, 1, size, dest);
}
fclose(source);
fclose(dest);
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
POSIX-WAY (K&Rでは、より低レベルのC言語(" The C programming language")でこれを使用します)
#include <iostream>
#include <fcntl.h> // open
#include <unistd.h> // read, write, close
#include <cstdio> // BUFSIZ
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
// BUFSIZE defaults to 8192
// BUFSIZE of 1 means one chareter at time
// good values should fit to blocksize, like 1024 or 4096
// higher values reduce number of system calls
// size_t BUFFER_SIZE = 4096;
char buf[BUFSIZ];
size_t size;
int source = open("from.ogv", O_RDONLY, 0);
int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644);
while ((size = read(source, buf, BUFSIZ)) > 0) {
write(dest, buf, size);
}
close(source);
close(dest);
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
KISS-C++-Streambuffer-WAY
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
ifstream source("from.ogv", ios::binary);
ofstream dest("to.ogv", ios::binary);
dest << source.rdbuf();
source.close();
dest.close();
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
コピーアルゴリズム-c++-方法
#include <iostream>
#include <fstream>
#include <ctime>
#include <algorithm>
#include <iterator>
using namespace std;
int main() {
clock_t start, end;
start = clock();
ifstream source("from.ogv", ios::binary);
ofstream dest("to.ogv", ios::binary);
istreambuf_iterator<char> begin_source(source);
istreambuf_iterator<char> end_source;
ostreambuf_iterator<char> begin_dest(dest);
copy(begin_source, end_source, begin_dest);
source.close();
dest.close();
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
自バッファ-c++-way
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
ifstream source("from.ogv", ios::binary);
ofstream dest("to.ogv", ios::binary);
// file size
source.seekg(0, ios::end);
ifstream::pos_type size = source.tellg();
source.seekg(0);
// allocate memory for buffer
char* buffer = new char[size];
// copy file
source.read(buffer, size);
dest.write(buffer, size);
// clean up
delete[] buffer;
source.close();
dest.close();
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
LINUX-WAY // カーネル >= 2.6.33 が必要です。
#include <iostream>
#include <sys/sendfile.h> // sendfile
#include <fcntl.h> // open
#include <unistd.h> // close
#include <sys/stat.h> // fstat
#include <sys/types.h> // fstat
#include <ctime>
using namespace std;
int main() {
clock_t start, end;
start = clock();
int source = open("from.ogv", O_RDONLY, 0);
int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644);
// struct required, rationale: function stat() exists also
struct stat stat_source;
fstat(source, &stat_source);
sendfile(dest, source, 0, stat_source.st_size);
close(source);
close(dest);
end = clock();
cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
cout << "CPU-TIME START " << start << "\n";
cout << "CPU-TIME END " << end << "\n";
cout << "CPU-TIME END - START " << end - start << "\n";
cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
環境
- GNU/LINUX (Archlinux)
- カーネル3.3
- GLIBC-2.15、LIBSTDC++ 4.7 (GCC-LIBS)、GCC 4.7、Coreutils 8.16
- RUNLEVEL 3を使用(マルチユーザー、ネットワーク、ターミナル、GUIなし)
- INTEL SSD-Postville 80GB、50%まで充填。
- 270MBのOGG-VIDEO-FILEをコピーする。
再現のための手順
1. $ rm from.ogg
2. $ reboot # kernel and filesystem buffers are in regular
3. $ (time ./program) &>> report.txt # executes program, redirects output of program and append to file
4. $ sha256sum *.ogv # checksum
5. $ rm to.ogg # remove copy, but no sync, kernel and fileystem buffers are used
6. $ (time ./program) &>> report.txt # executes program, redirects output of program and append to file
結果(使用CPU TIME)
Program Description UNBUFFERED|BUFFERED
ANSI C (fread/frwite) 490,000|260,000
POSIX (K&R, read/write) 450,000|230,000
FSTREAM (KISS, Streambuffer) 500,000|270,000
FSTREAM (Algorithm, copy) 500,000|270,000
FSTREAM (OWN-BUFFER) 500,000|340,000
SENDFILE (native LINUX, sendfile) 410,000|200,000
ファイルサイズは変わりません。
sha256sumは同じ結果を表示します。
ビデオファイルはまだ再生可能です。
質問内容
- どのような方式が良いですか?
- より良い解決策をご存知ですか?
- 私のコードに間違いはありませんか?
-
解決策を回避する理由をご存知ですか?
-
FSTREAM (KISS、Streambuffer)
これは本当に短くてシンプルなので、とても気に入っています。私が知る限り、演算子 << は rdbuf() のためにオーバーロードされたもので、何も変換しません。正しいですか?
ありがとうございます。
アップデート1
の測定にファイルディスクリプタの開閉が含まれるように、全サンプルでソースを変更しました。
クロック()
. その他、ソースコードに大きな変更はありません。結果も変わりません。また
時間
でダブルチェック。
アップデート2
ANSI Cのサンプルが変更されました。
whileループ
はもう呼び出されません。
feof()
その代わりに
fread()
を条件に入れています。見た目には、コードの実行が1万クロック速くなったように見えます。
測定値の変更:以前の結果は常にバッファリングされていました。 rm to.ogv && sync && time ./program を各番組ごとに数回繰り返す。今は、プログラムごとにシステムを再起動しています。バッファリングされていない結果は新しいもので、何の驚きも示していません。バッファリングされていない結果は本当に変わりませんでした。
古いコピーを削除しない場合、プログラムの反応は異なります。既存のファイルを上書きする バッファリング はPOSIXとSENDFILEで速く、他のすべてのプログラムは遅くなります。もしかしたら、オプションの 切り捨てる または 作成 は、この動作に影響を与えます。しかし、既存のファイルを同じコピーで上書きすることは、現実的なユースケースとは言えません。
を使用してコピーを実行します。 シーピー はアンバッファリングで0.44秒、バッファリングで0.30秒かかります。そのため シーピー はPOSIXのサンプルより少し遅いです。私には問題なさそうです。
のサンプルと結果も追加しようかな。
mmap()
と
copy_file()
をboost::filesystemから取得しました。
アップデート3
これもブログページに載せて少し拡張しました。含まれるもの
splice()
これはLinuxカーネルの低レベル関数です。多分、Javaを使ったサンプルはもっと続くでしょう。
http://www.ttyhoney.com/blog/?page_id=69
解決方法は?
ファイルをまともにコピーする。
#include <fstream>
int main()
{
std::ifstream src("from.ogv", std::ios::binary);
std::ofstream dst("to.ogv", std::ios::binary);
dst << src.rdbuf();
}
これはとてもシンプルで直感的に読めるので、追加コストをかける価値があります。もし、それを何度も行うのであれば、OSのファイルシステムへの呼び出しにフォールバックするのがベターでしょう。きっと
boost
は、そのファイルシステム・クラスにコピー・ファイル・メソッドを持っています。
ファイルシステムと対話するためのCメソッドがある。
#include <copyfile.h>
int
copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
関連
-
[解決済み] クラスにデフォルトコンストラクタが存在しない。
-
[解決済み] 式はクラス型を持つ必要があります。
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み] Git リポジトリで削除されたファイルを検索して復元する方法
-
[解決済み] コピーアンドスワップ慣用句とは?
-
[解決済み] Microsoft Officeをインストールせずに、C#でExcel(.XLSおよび.XLSX)ファイルを作成するにはどうすればよいですか?
-
[解決済み] ファイルを作成し、書き込むにはどうすればよいですか?
-
[解決済み] 出力をファイルや標準出力にリダイレクトする方法
-
[解決済み] ファイルに行を書き込む正しい方法?
-
[解決済み] ファイルが使用中であるかどうかを確認する方法はありますか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++エラー。アーキテクチャ x86_64 に対して未定義のシンボル
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み】1つ以上の多重定義されたシンボルが見つかる
-
[解決済み】std::cin.getline( ) vs. std::cin
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み] 配列のベクトルを扱う正しい方法
-
[解決済み] time(1) の出力における 'real', 'user' および 'sys' はどのような意味ですか?
-
[解決済み】C++で大きなバッファをバイナリファイルに高速に書き込むには?