C++界隈で約20年ダメプログラマをやっているまさじぃです。
今回はfstream
を使ってファイルを連結したいと思います。
C++でファイルを連結するには、基本的に連結したいファイルを読み込みモードで開いて、書き込みモードで開いた新しいファイルに書き出すかたちになります。
fstreamでファイル内容を一括読み取りする
fstream
のrdbuf()
を使用してファイル内容を一括で読み取って新しいファイルに書き出す方法です。
#include <fstream>
int main() {
std::ifstream file1("file_name.txt", std::ios::binary);
std::ifstream file2("file_name2.txt", std::ios::binary);
std::ofstream output("output.txt", std::ios::binary);
output << file1.rdbuf() << file2.rdbuf();
file1.close();
file2.close();
output.close();
return 0;
}
連結したいファイルをifstream
、連結後のファイルをofstream
で宣言して連結後のファイルに書き出します。
ファイルの連結をしたい場合は、ファイルAの後ろにファイルBを連結してファイルAを残してファイルBは消したいなんてこともあると思います。
以下のように、一旦連結ファイルを作成して、元ファイルを削除した後に連結ファイルをリネームすると実現できます。
#include <fstream>
#include <cstdio>
int main() {
std::string file_name_1 = "file_name.txt";
std::string file_name_2 = "file_name2.txt";
std::string output_name = "output.txt";
std::ifstream file1(file_name_1, std::ios::binary);
std::ifstream file2(file_name_2, std::ios::binary);
std::ofstream output(output_name, std::ios::binary);
output << file1.rdbuf() << file2.rdbuf();
file1.close();
file2.close();
output.close();
// 連結ファイルを作成したので元ファイルは削除する
std::remove(file_name_1.c_str());
std::remove(file_name_2.c_str());
// ファイル名変更
std::rename(output_name.c_str(), file_name_1.c_str());
return 0;
}
ファイル内を読み込みながら連結する
タイトルや項目名などの固定の行があるファイルを連結する場合、一括で連結した場合変な位置にタイトルや項目名が入ってしまいます。
そんな場合は、ファイルを開きファイル内容を読み込みながら出力先ファイルへ書き出していくとできます。
以下のようなファイルがあると想定します。
【ファイル1】
No,Name,Old,Job
001,James,32,Financial Analyst
002,Michael,45,Project Manager
003,Daniel,25,Software Engineer
【ファイル2】
No,Name,Old,Job
008,Sophia,38,Graphic Designer
010,Isabella,29,Mechanical Engineer
各ファイルの1行目にあるタイトル行は途中で登場してほしくないので、出力先ファイルには最初の1回だけ出したいので1行ずつ読み込んでいきます。
#include <fstream>
#include <cstdio>
int main() {
std::string file_name_1 = "file_1.txt";
std::string file_name_2 = "file_2.txt";
std::string output_name = "output.txt";
std::ifstream file1(file_name_1);
std::ifstream file2(file_name_2);
std::ofstream output(output_name, std::ios::out | std::ios::trunc);
// 1つ目のファイルを読み込みながら出力ファイルに書き出す
if (file1) {
std::string line;
while (std::getline(file1, line)) {
output << line << std::endl;
}
} else {
std::cout << "Failed to open " << file_name_1 << std::endl;
}
// 2つ目のファイルを読み込みながら出力ファイルに書き出す
if (file2) {
std::string line;
std::getline(file2, line); // 2つ目のファイルでは先頭のタイトル行は不要なので読み捨て
while (std::getline(file2, line)) {
output << line << std::endl;
}
} else {
std::cout << "Failed to open " << file_name_2 << std::endl;
}
file1.close();
file2.close();
output.close();
// 連結ファイルを作成したので元ファイルは削除する
std::remove(file_name_1.c_str());
std::remove(file_name_2.c_str());
// ファイル名変更
std::rename(output_name.c_str(), file_name_1.c_str());
return 0;
}
2つ目のファイルを読み込む時に先頭のタイトル行を読み捨てたいので、ファイルを読み書きしているwhile
文より前で1回だけgetline()
を実行しています。
複数ファイルを連結する
今までのパターンは2つのファイルを連結しているだけでしたが、3つ以上のファイルを連結したいこともあります。
連結したいファイルをstd::vector
を使って動的配列として、for
文で回すことで複数ファイルの連結を行います。
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
int main() {
// 入力ファイルのリスト
std::vector<std::string> inputFiles = {"file_1.txt", "file_2.txt", "file_3.txt"};
std::string outputFile = "output.txt";
std::ofstream out(outputFile, std::ios::out | std::ios::trunc);
for (const auto& fileName : inputFiles) {
std::ifstream inFile(fileName);
if (inFile) {
std::string line;
while (std::getline(inFile, line)) {
out << line << std::endl;
}
} else {
std::cout << "Failed to open " << fileName << std::endl;
}
}
return 0;
}