ファイル

[C++] ファイルをコピーする方法(ファイル/シンボリックリンク)

C++でファイルをコピーするには、標準ライブラリのstd::filesystem::copyを使用します。

この関数はC++17以降で利用可能で、ファイルやディレクトリのコピーが可能です。

コピーの動作はstd::filesystem::copy_optionsで制御でき、通常のファイルコピーやシンボリックリンクのコピーを指定できます。

例えば、copy_options::noneは通常のコピー、copy_options::copy_symlinksはシンボリックリンク自体をコピーします。

C++でファイルをコピーする方法

C++でファイルをコピーするには、標準ライブラリの機能を利用することができます。

ここでは、基本的なファイルコピーの方法を解説します。

ファイルの読み込みと書き込みを行うために、<fstream>ヘッダを使用します。

以下に、ファイルをコピーするためのサンプルコードを示します。

#include <iostream>
#include <fstream>
#include <string>
void copyFile(const std::string& source, const std::string& destination) {
    std::ifstream src(source, std::ios::binary); // ソースファイルをバイナリモードで開く
    std::ofstream dest(destination, std::ios::binary); // デスティネーションファイルをバイナリモードで開く
    if (!src) { // ソースファイルが開けない場合
        std::cerr << "ソースファイルを開けません: " << source << std::endl;
        return;
    }
    if (!dest) { // デスティネーションファイルが開けない場合
        std::cerr << "デスティネーションファイルを開けません: " << destination << std::endl;
        return;
    }
    dest << src.rdbuf(); // ソースファイルの内容をデスティネーションファイルにコピー
}
int main() {
    std::string sourceFile = "source.txt"; // コピー元ファイル名
    std::string destinationFile = "destination.txt"; // コピー先ファイル名
    copyFile(sourceFile, destinationFile); // ファイルコピーを実行
    return 0;
}
(特に出力はありませんが、destination.txtにsource.txtの内容がコピーされます)

このコードでは、copyFile関数を定義し、ソースファイルとデスティネーションファイルのパスを引数として受け取ります。

ファイルをバイナリモードで開き、エラーチェックを行った後、ソースファイルの内容をデスティネーションファイルにコピーします。

シンボリックリンクのコピー方法

C++でシンボリックリンクをコピーするには、標準ライブラリだけではなく、プラットフォームに依存したAPIを使用する必要があります。

ここでは、POSIX準拠のシステム(LinuxやmacOSなど)でのシンボリックリンクのコピー方法を解説します。

Windowsの場合は、別途APIを使用する必要があります。

以下に、シンボリックリンクをコピーするためのサンプルコードを示します。

#include <iostream>
#include <unistd.h> // symlink, readlink
#include <string.h> // strerror
#include <errno.h> // errno
void copySymbolicLink(const std::string& source, const std::string& destination) {
    char targetPath[1024]; // ターゲットパスを格納するバッファ
    ssize_t len = readlink(source.c_str(), targetPath, sizeof(targetPath) - 1); // シンボリックリンクの内容を読み取る
    if (len == -1) { // シンボリックリンクが開けない場合
        std::cerr << "シンボリックリンクを読み取れません: " << strerror(errno) << std::endl;
        return;
    }
    targetPath[len] = '\0'; // 読み取った内容を文字列として終端する
    if (symlink(targetPath, destination.c_str()) == -1) { // 新しいシンボリックリンクを作成
        std::cerr << "シンボリックリンクをコピーできません: " << strerror(errno) << std::endl;
    }
}
int main() {
    std::string sourceLink = "source_link"; // コピー元シンボリックリンク名
    std::string destinationLink = "destination_link"; // コピー先シンボリックリンク名
    copySymbolicLink(sourceLink, destinationLink); // シンボリックリンクコピーを実行
    return 0;
}
(特に出力はありませんが、destination_linkがsource_linkを指すシンボリックリンクとして作成されます)

このコードでは、copySymbolicLink関数を定義し、ソースシンボリックリンクとデスティネーションシンボリックリンクのパスを引数として受け取ります。

readlink関数を使用してシンボリックリンクの内容を読み取り、symlink関数を使用して新しいシンボリックリンクを作成します。

エラーチェックも行い、問題があればエラーメッセージを表示します。

エラーハンドリングと例外処理

C++でファイルコピーやシンボリックリンクの操作を行う際には、エラーハンドリングと例外処理が重要です。

これにより、プログラムが予期しない状況に対処できるようになります。

以下に、ファイルコピーの例を用いてエラーハンドリングと例外処理を実装する方法を示します。

#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept> // std::runtime_error
void copyFile(const std::string& source, const std::string& destination) {
    std::ifstream src(source, std::ios::binary); // ソースファイルをバイナリモードで開く
    std::ofstream dest(destination, std::ios::binary); // デスティネーションファイルをバイナリモードで開く
    if (!src) { // ソースファイルが開けない場合
        throw std::runtime_error("ソースファイルを開けません: " + source);
    }
    if (!dest) { // デスティネーションファイルが開けない場合
        throw std::runtime_error("デスティネーションファイルを開けません: " + destination);
    }
    dest << src.rdbuf(); // ソースファイルの内容をデスティネーションファイルにコピー
}
int main() {
    std::string sourceFile = "source.txt"; // コピー元ファイル名
    std::string destinationFile = "destination.txt"; // コピー先ファイル名
    try {
        copyFile(sourceFile, destinationFile); // ファイルコピーを実行
        std::cout << "ファイルコピーが成功しました。" << std::endl;
    } catch (const std::runtime_error& e) { // 例外をキャッチ
        std::cerr << "エラー: " << e.what() << std::endl; // エラーメッセージを表示
    }
    return 0;
}
(成功した場合)
ファイルコピーが成功しました。
(失敗した場合)
エラー: ソースファイルを開けません: source.txt

このコードでは、copyFile関数内でファイルのオープンに失敗した場合にstd::runtime_errorをスローします。

main関数では、tryブロックを使用してcopyFileを呼び出し、例外が発生した場合にはcatchブロックでエラーメッセージを表示します。

このようにすることで、プログラムが異常終了することなく、エラーを適切に処理することができます。

応用的なファイルコピーのテクニック

C++でのファイルコピーには、基本的な方法以外にもさまざまな応用的なテクニックがあります。

ここでは、以下の3つのテクニックを紹介します。

  1. 進捗状況の表示
  2. 大きなファイルの分割コピー
  3. ファイル属性のコピー

進捗状況の表示

ファイルコピーの進捗状況を表示することで、ユーザーに進行状況を知らせることができます。

以下のサンプルコードでは、コピーするバイト数をカウントし、進捗を表示します。

#include <iostream>
#include <fstream>
#include <string>
void copyFileWithProgress(const std::string& source, const std::string& destination) {
    std::ifstream src(source, std::ios::binary);
    std::ofstream dest(destination, std::ios::binary);
    if (!src || !dest) {
        std::cerr << "ファイルを開けません。" << std::endl;
        return;
    }
    src.seekg(0, std::ios::end); // ソースファイルのサイズを取得
    std::streamsize totalSize = src.tellg(); // 総バイト数
    src.seekg(0, std::ios::beg); // ファイルポインタを先頭に戻す
    std::streamsize copiedSize = 0;
    char buffer[1024]; // バッファを定義
    while (src.read(buffer, sizeof(buffer))) {
        dest.write(buffer, src.gcount()); // 読み取ったバイト数を書き込む
        copiedSize += src.gcount(); // コピーしたバイト数を更新
        std::cout << "進捗: " << (static_cast<double>(copiedSize) / totalSize) * 100 << "%\r"; // 進捗を表示
    }
    dest.write(buffer, src.gcount()); // 残りのデータを書き込む
    std::cout << "コピー完了。" << std::endl;
}
int main() {
    std::string sourceFile = "large_file.txt"; // 大きなファイル名
    std::string destinationFile = "copied_file.txt"; // コピー先ファイル名
    copyFileWithProgress(sourceFile, destinationFile); // 進捗表示付きファイルコピーを実行
    return 0;
}
進捗: 50.0%
コピー完了。

大きなファイルの分割コピー

大きなファイルを分割してコピーすることで、メモリの使用量を抑えることができます。

以下のサンプルコードでは、指定したサイズごとにファイルを分割してコピーします。

#include <iostream>
#include <fstream>
#include <string>
void copyFileInChunks(const std::string& source, const std::string& destination, std::streamsize chunkSize) {
    std::ifstream src(source, std::ios::binary);
    std::ofstream dest(destination, std::ios::binary);
    if (!src || !dest) {
        std::cerr << "ファイルを開けません。" << std::endl;
        return;
    }
    char* buffer = new char[chunkSize]; // 指定サイズのバッファを動的に確保
    while (src.read(buffer, chunkSize) || src.gcount() > 0) {
        dest.write(buffer, src.gcount()); // 読み取ったバイト数を書き込む
    }
    delete[] buffer; // バッファを解放
}
int main() {
    std::string sourceFile = "large_file.txt"; // 大きなファイル名
    std::string destinationFile = "copied_file.txt"; // コピー先ファイル名
    std::streamsize chunkSize = 1024 * 1024; // 1MBごとにコピー
    copyFileInChunks(sourceFile, destinationFile, chunkSize); // 分割コピーを実行
    return 0;
}
(特に出力はありませんが、copied_file.txtにlarge_file.txtの内容がコピーされます)

ファイル属性のコピー

ファイルのコピー時に、ファイルの属性(パーミッションやタイムスタンプなど)をコピーすることも重要です。

以下のサンプルコードでは、POSIXシステムでのファイル属性のコピーを示します。

#include <iostream>
#include <fstream>
#include <string>
#include <sys/stat.h> // stat, chmod, utime
void copyFileWithAttributes(const std::string& source, const std::string& destination) {
    std::ifstream src(source, std::ios::binary);
    std::ofstream dest(destination, std::ios::binary);
    if (!src || !dest) {
        std::cerr << "ファイルを開けません。" << std::endl;
        return;
    }
    dest << src.rdbuf(); // ファイル内容をコピー
    struct stat fileStat;
    if (stat(source.c_str(), &fileStat) == 0) { // ソースファイルの属性を取得
        chmod(destination.c_str(), fileStat.st_mode); // パーミッションをコピー
        utime(destination.c_str(), &fileStat.st_atime); // アクセスタイムをコピー
    }
}
int main() {
    std::string sourceFile = "source.txt"; // コピー元ファイル名
    std::string destinationFile = "destination.txt"; // コピー先ファイル名
    copyFileWithAttributes(sourceFile, destinationFile); // 属性付きファイルコピーを実行
    return 0;
}
(特に出力はありませんが、destination.txtにsource.txtの内容がコピーされ、属性もコピーされます)

これらの応用的なテクニックを使用することで、C++でのファイルコピーをより効率的かつ柔軟に行うことができます。

他のライブラリを使ったファイルコピー

C++では、標準ライブラリ以外にもファイルコピーを行うための便利なライブラリがいくつか存在します。

ここでは、以下の2つのライブラリを紹介します。

  1. Boost.Filesystem
  2. C++17のstd::filesystem

Boost.Filesystem

Boostライブラリの一部であるBoost.Filesystemは、ファイルシステムに関する操作を簡単に行うための機能を提供します。

以下に、Boost.Filesystemを使用してファイルをコピーするサンプルコードを示します。

#include <iostream>
#include <boost/filesystem.hpp> // Boost.Filesystemヘッダ
void copyFileWithBoost(const std::string& source, const std::string& destination) {
    try {
        boost::filesystem::copy_file(source, destination); // ファイルをコピー
        std::cout << "ファイルがコピーされました: " << destination << std::endl;
    } catch (const boost::filesystem::filesystem_error& e) {
        std::cerr << "エラー: " << e.what() << std::endl; // エラーメッセージを表示
    }
}
int main() {
    std::string sourceFile = "source.txt"; // コピー元ファイル名
    std::string destinationFile = "destination.txt"; // コピー先ファイル名
    copyFileWithBoost(sourceFile, destinationFile); // Boostを使ったファイルコピーを実行
    return 0;
}
ファイルがコピーされました: destination.txt

Boost.Filesystemを使用することで、ファイルコピーの処理が非常にシンプルになります。

エラーハンドリングもライブラリが提供する機能を利用して行います。

C++17のstd::filesystem

C++17からは、標準ライブラリにstd::filesystemが追加され、ファイルシステムに関する操作が標準でサポートされるようになりました。

以下に、std::filesystemを使用してファイルをコピーするサンプルコードを示します。

#include <iostream>
#include <filesystem> // std::filesystemヘッダ
void copyFileWithStdFilesystem(const std::string& source, const std::string& destination) {
    try {
        std::filesystem::copy(source, destination); // ファイルをコピー
        std::cout << "ファイルがコピーされました: " << destination << std::endl;
    } catch (const std::filesystem::filesystem_error& e) {
        std::cerr << "エラー: " << e.what() << std::endl; // エラーメッセージを表示
    }
}
int main() {
    std::string sourceFile = "source.txt"; // コピー元ファイル名
    std::string destinationFile = "destination.txt"; // コピー先ファイル名
    copyFileWithStdFilesystem(sourceFile, destinationFile); // std::filesystemを使ったファイルコピーを実行
    return 0;
}
ファイルがコピーされました: destination.txt

std::filesystemを使用することで、C++の標準機能としてファイルコピーが可能になります。

これにより、外部ライブラリに依存せずに、より簡潔にファイル操作を行うことができます。

これらのライブラリを活用することで、C++でのファイルコピーをより効率的に行うことができ、コードの可読性や保守性も向上します。

まとめ

この記事では、C++を使用したファイルコピーの基本的な方法から、シンボリックリンクのコピー、エラーハンドリング、応用的なテクニック、さらにはBoostやC++17の標準ライブラリを利用したファイルコピーの方法まで幅広く解説しました。

これにより、さまざまな状況に応じたファイル操作が可能になるでしょう。

今後は、実際のプロジェクトでこれらの技術を活用し、効率的なファイル管理を実現してみてください。

関連記事

Back to top button