サーチ…
前書き
C ++ファイルの入出力はストリーム経由で行われます 。主要な抽象化は次のとおりです。
テキストを読むためのstd::istream
。
テキストを書くためのstd::ostream
。
std::streambuf
をstd::streambuf
て文字を読み書きします。
書式付き入力はoperator>>
使用しoperator>>
。
フォーマットされた出力はoperator<<
使用します。
ストリームは、フォーマットの詳細や外部エンコーディングと内部エンコーディングの間の変換のために、例えばstd::locale
使用しstd::locale
。
ストリームの詳細: <iostream>ライブラリ
ファイルを開く
ファイルを開くことは、3つのファイルストリーム( ifstream
、 ofstream
、およびfstream
)すべてに対して同じ方法で行われます。
コンストラクタで直接ファイルを開くことができます:
std::ifstream ifs("foo.txt"); // ifstream: Opens file "foo.txt" for reading only.
std::ofstream ofs("foo.txt"); // ofstream: Opens file "foo.txt" for writing only.
std::fstream iofs("foo.txt"); // fstream: Opens file "foo.txt" for reading and writing.
または、ファイルストリームのメンバ関数open()
を使用することもできます。
std::ifstream ifs;
ifs.open("bar.txt"); // ifstream: Opens file "bar.txt" for reading only.
std::ofstream ofs;
ofs.open("bar.txt"); // ofstream: Opens file "bar.txt" for writing only.
std::fstream iofs;
iofs.open("bar.txt"); // fstream: Opens file "bar.txt" for reading and writing.
ファイルが正常に開かれたかどうか(書き込み中であっても) 常に確認する必要があります。障害には、ファイルが存在しない、ファイルが適切なアクセス権を持たない、ファイルがすでに使用中である、ディスクエラーが発生した、ドライブが切断されている、などが含まれます。
// Try to read the file 'foo.txt'.
std::ifstream ifs("fooo.txt"); // Note the typo; the file can't be opened.
// Check if the file has been opened successfully.
if (!ifs.is_open()) {
// The file hasn't been opened; take appropriate actions here.
throw CustomException(ifs, "File could not be opened");
}
ファイルパスにバックスラッシュが含まれている場合(Windowsシステムなど)、適切にエスケープする必要があります。
// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:\\folder\\foo.txt"); // using escaped backslashes
生のリテラルを使用する:
// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs(R"(c:\folder\foo.txt)"); // using raw literal
代わりにスラッシュを使用してください。
// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:/folder/foo.txt");
現在、Windows上のパスに非ASCII文字を含むファイルを開く場合は、 非標準のワイド文字パス引数を使用できます。
// Open the file 'пример\foo.txt' on Windows.
std::ifstream ifs(LR"(пример\foo.txt)"); // using wide characters with raw literal
ファイルからの読み込み
ファイルからデータを読み取るにはいくつかの方法があります。
データのフォーマット方法がわかっている場合は、ストリーム抽出演算子( >>
)を使用できます。次のデータを含むfoo.txtという名前のファイルがあるとします。
John Doe 25 4 6 1987
Jane Doe 15 5 24 1976
次に、次のコードを使用してファイルからそのデータを読み取ることができます。
// Define variables.
std::ifstream is("foo.txt");
std::string firstname, lastname;
int age, bmonth, bday, byear;
// Extract firstname, lastname, age, bday month, bday day, and bday year in that order.
// Note: '>>' returns false if it reached EOF (end of file) or if the input data doesn't
// correspond to the type of the input variable (for example, the string "foo" can't be
// extracted into an 'int' variable).
while (is >> firstname >> lastname >> age >> bmonth >> bday >> byear)
// Process the data that has been read.
ストリーム抽出演算子>>
すべての文字を抽出し、格納できない文字や特殊文字であれば停止します。
- 文字列型の場合、演算子は空白(
)または改行(
\n
)で。 - 数字の場合、演算子は数字以外の文字で停止します。
つまり、 foo.txtファイルの次のバージョンも前のコードで正常に読み込まれます。
John
Doe 25
4 6 1987
Jane
Doe
15 5
24
1976
ストリーム抽出演算子>>
常に与えられたストリームを返します。したがって、データを連続して読み取るために、複数の演算子を連鎖させることができます。ただし、ストリームをブール式として使用することもできます(前のコードのwhile
ループに示されています)。これは、ストリームクラスにはbool
型の変換演算子があるためです。このbool()
演算子は、ストリームにエラーがなければtrue
を返しtrue
。ストリームがエラー状態になると(例えば、それ以上データを抽出できないなど)、 bool()
演算子はfalse
を返しfalse
。したがって、前のコードのwhile
ループは、入力ファイルが最後まで読み込まれた後で終了します。
ファイル全体を文字列として読み取る場合は、次のコードを使用します。
// Opens 'foo.txt'.
std::ifstream is("foo.txt");
std::string whole_file;
// Sets position to the end of the file.
is.seekg(0, std::ios::end);
// Reserves memory for the file.
whole_file.reserve(is.tellg());
// Sets position to the start of the file.
is.seekg(0, std::ios::beg);
// Sets contents of 'whole_file' to all characters in the file.
whole_file.assign(std::istreambuf_iterator<char>(is),
std::istreambuf_iterator<char>());
このコードでは、不要なメモリ割り当てを減らすためにstring
スペースを予約していstring
。
ファイルを1行getline()
読みたい場合は、関数getline()
使うことができます:
std::ifstream is("foo.txt");
// The function getline returns false if there are no more lines.
for (std::string str; std::getline(is, str);) {
// Process the line that has been read.
}
固定数の文字を読みたい場合は、ストリームのメンバ関数read()
使用することができます。
std::ifstream is("foo.txt");
char str[4];
// Read 4 characters from the file.
is.read(str, 4);
読み取りコマンドを実行した後は、エラー状態フラグfailbit
が設定されているかどうか、操作が失敗したかどうかを常に確認する必要があります。これはファイルストリームのメンバ関数fail()
呼び出すことで行うことができます:
is.read(str, 4); // This operation might fail for any reason.
if (is.fail())
// Failed to read!
ファイルへの書き込み
ファイルに書き込むにはいくつかの方法があります。最も簡単な方法は、出力ファイルストリーム( ofstream
)をストリーム挿入演算子( <<
)と共に使用することです。
std::ofstream os("foo.txt");
if(os.is_open()){
os << "Hello World!";
}
<<
代わりに、出力ファイルストリームのメンバ関数write()
使用することもできます。
std::ofstream os("foo.txt");
if(os.is_open()){
char data[] = "Foo";
// Writes 3 characters from data -> "Foo".
os.write(data, 3);
}
ストリームに書き込んだ後、エラー状態フラグbadbit
が設定されているかどうかは、操作が失敗したかどうかを示すため、常に確認する必要があります。これは、出力ファイルストリームのメンバ関数bad()
呼び出すことによって行うことができます。
os << "Hello Badbit!"; // This operation might fail for any reason.
if (os.bad())
// Failed to write!
オープニングモード
ファイルストリームを作成するときに、開くモードを指定できます。開始モードは、基本的にストリームがファイルを開く方法を制御する設定です。
(すべてのモードはstd::ios
名前空間にあります。)
開始モードは、ファイルストリームのコンストラクタまたはそのopen()
メンバ関数の第2パラメータとして提供することができます。
std::ofstream os("foo.txt", std::ios::out | std::ios::trunc);
std::ifstream is;
is.open("foo.txt", std::ios::in | std::ios::binary);
正しいデフォルト値を持っていても、iostreamメンバーによって暗黙的に設定されていない他のフラグを設定するには、 ios::in
またはios::out
を設定する必要があることに注意してください。
開始モードを指定しないと、次のデフォルトモードが使用されます。
-
ifstream
-in
-
ofstream
out
-
fstream
-in
とout
デザインで指定できるファイルオープンモードは次のとおりです。
モード | 意味 | にとって | 説明 |
---|---|---|---|
app | 追加する | 出力 | ファイルの末尾にデータを追加します。 |
binary | バイナリ | 入出力 | 入力と出力はバイナリで行われます。 |
in | 入力 | 入力 | 読み込むためにファイルを開きます。 |
out | 出力 | 出力 | 書き込み用のファイルを開きます。 |
trunc | 切り詰める | 入出力 | 開くときにファイルの内容を削除します。 |
ate | 最後に | 入力 | 開くときにファイルの最後に移動します。 |
注: binary
モードを設定すると、データをそのまま読み書きすることができます。それを設定しないと、改行'\n'
文字をプラットフォーム固有の行末シーケンスとの間で変換することができます。
たとえば、Windowsでは、行末の順序はCRLF( "\r\n"
)です。
書き込み: "\n"
=> "\r\n"
読んでください: "\r\n"
=> "\n"
ファイルを閉じる
明示的にファイルを閉じることは、C ++ではほとんど必要ありません。ファイルストリームはデストラクタ内の関連するファイルを自動的に閉じるためです。ただし、必要以上にファイルハンドルを開いたままにしないように、ファイルストリームオブジェクトの有効期間を制限するようにしてください。たとえば、これは、すべてのファイル操作を独自のスコープ( {}
)に入れることによって実行できます。
std::string const prepared_data = prepare_data();
{
// Open a file for writing.
std::ofstream output("foo.txt");
// Write data.
output << prepared_data;
} // The ofstream will go out of scope here.
// Its destructor will take care of closing the file properly.
close()
明示的に呼び出すことは、後で同じfstream
オブジェクトを再利用したいが、その間にファイルを開いたままにしたくない場合にのみ必要です。
// Open the file "foo.txt" for the first time.
std::ofstream output("foo.txt");
// Get some data to write from somewhere.
std::string const prepared_data = prepare_data();
// Write data to the file "foo.txt".
output << prepared_data;
// Close the file "foo.txt".
output.close();
// Preparing data might take a long time. Therefore, we don't open the output file stream
// before we actually can write some data to it.
std::string const more_prepared_data = prepare_complex_data();
// Open the file "foo.txt" for the second time once we are ready for writing.
output.open("foo.txt");
// Write the data to the file "foo.txt".
output << more_prepared_data;
// Close the file "foo.txt" once again.
output.close();
ストリームをフラッシュする
多くの他のタイプのストリームと同様に、ファイルストリームはデフォルトでバッファリングされます。つまり、ストリームへの書き込みによって、基になるファイルがすぐに変更されることはありません。すべてのバッファリングされた書き込みが強制的に強制的に実行されるようにするには、ストリームをフラッシュできます。これは、 flush()
メソッドを呼び出すか、 std::flush
ストリームマニピュレータを使用して直接行うことができます。
std::ofstream os("foo.txt");
os << "Hello World!" << std::flush;
char data[3] = "Foo";
os.write(data, 3);
os.flush();
ストリームをフラッシュして改行を書くことを組み合わせたストリームマニピュレータstd::endl
があります:
// Both following lines do the same thing
os << "Hello World!\n" << std::flush;
os << "Hello world!" << std::endl;
バッファリングは、ストリームへの書き込みパフォーマンスを向上させることができます。したがって、多くの書き込みを行うアプリケーションでは、不要なフラッシュを避ける必要があります。逆に、I / Oが頻繁に行われない場合、ストリームオブジェクトにデータが滞留しないように、アプリケーションは頻繁にフラッシングを考慮する必要があります。
ASCIIファイルをstd :: stringに読み込む
std::ifstream f("file.txt");
if (f)
{
std::stringstream buffer;
buffer << f.rdbuf();
f.close();
// The content of "file.txt" is available in the string `buffer.str()`
}
rdbuf()
メソッドは、 rdbuf()
stringstream::operator<<
メンバ関数を介してbuffer
にプッシュできるstreambuf
へのポインタを返します。
別の可能性( Scott Meyersの Effective STLで普及している)は次のとおりです。
std::ifstream f("file.txt");
if (f)
{
std::string str((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
// Operations on `str`...
}
これは、コードをほとんど必要としない(文字列だけでなく、任意のSTLコンテナに直接ファイルを読み込むことも可能ですが)大きなファイルでは遅くなる可能性があるため、いいです。
注 :文字列コンストラクタへの最初の引数の周りの余分なカッコは、 最も厄介な解析問題を防ぐために不可欠です。
少なくとも最後のではなく:
std::ifstream f("file.txt");
if (f)
{
f.seekg(0, std::ios::end);
const auto size = f.tellg();
std::string str(size, ' ');
f.seekg(0);
f.read(&str[0], size);
f.close();
// Operations on `str`...
}
これはおそらく最も速い選択肢(提案された3つの中で)でしょう。
ファイルをコンテナに読み込む
以下の例では、 std::string
とoperator>>
を使ってファイルから項目を読み込みます。
std::ifstream file("file3.txt");
std::vector<std::string> v;
std::string s;
while(file >> s) // keep reading until we run out
{
v.push_back(s);
}
上記の例では、 operator>>
を使って、一度に1つの「アイテム」を読み取るファイルを単純に反復しています。この同じ効果は、ストリームから一度に1つの「アイテム」を読み込む入力イテレータであるstd::istream_iterator
を使用して達成できます。また、ほとんどのコンテナは2つのイテレータを使用して構築することができるので、上記のコードを単純化すると次のようになります。
std::ifstream file("file3.txt");
std::vector<std::string> v(std::istream_iterator<std::string>{file},
std::istream_iterator<std::string>{});
これを拡張して、 std::istream_iterator
へのテンプレートパラメータとして読み込みたいオブジェクトを指定するだけで、好きなオブジェクト型を読み込むことができます。このように、上記のように、以下のような行(単語ではなく)を読み込むことができます:
// Unfortunately there is no built in type that reads line using >>
// So here we build a simple helper class to do it. That will convert
// back to a string when used in string context.
struct Line
{
// Store data here
std::string data;
// Convert object to string
operator std::string const&() const {return data;}
// Read a line from a stream.
friend std::istream& operator>>(std::istream& stream, Line& line)
{
return std::getline(stream, line.data);
}
};
std::ifstream file("file3.txt");
// Read the lines of a file into a container.
std::vector<std::string> v(std::istream_iterator<Line>{file},
std::istream_iterator<Line>{});
フォーマットされたテキストファイルから `struct`を読み込みます。
struct info_type
{
std::string name;
int age;
float height;
// we define an overload of operator>> as a friend function which
// gives in privileged access to private data members
friend std::istream& operator>>(std::istream& is, info_type& info)
{
// skip whitespace
is >> std::ws;
std::getline(is, info.name);
is >> info.age;
is >> info.height;
return is;
}
};
void func4()
{
auto file = std::ifstream("file4.txt");
std::vector<info_type> v;
for(info_type info; file >> info;) // keep reading until we run out
{
// we only get here if the read succeeded
v.push_back(info);
}
for(auto const& info: v)
{
std::cout << " name: " << info.name << '\n';
std::cout << " age: " << info.age << " years" << '\n';
std::cout << "height: " << info.height << "lbs" << '\n';
std::cout << '\n';
}
}
file4.txt
Wogger Wabbit
2
6.2
Bilbo Baggins
111
81.3
Mary Poppins
29
154.8
出力:
name: Wogger Wabbit
age: 2 years
height: 6.2lbs
name: Bilbo Baggins
age: 111 years
height: 81.3lbs
name: Mary Poppins
age: 29 years
height: 154.8lbs
ファイルのコピー
std::ifstream src("source_filename", std::ios::binary);
std::ofstream dst("dest_filename", std::ios::binary);
dst << src.rdbuf();
C ++ 17では、ファイルをコピーするための標準的な方法は、 <filesystem>
ヘッダをcopy_file
を使用することcopy_file
。
std::fileystem::copy_file("source_filename", "dest_filename");
ファイルシステムライブラリはもともとboost.filesystem
として開発され、最終的にC ++ 17の時点でISO C ++にマージされboost.filesystem
。
ループ状態、悪い練習の中でファイルの終わりをチェックしていますか?
eof
は、ファイルの終わりを読んだ後でのみtrue
返します。次の読み込みがストリームの終わりになることを示すものではありません。
while (!f.eof())
{
// Everything is OK
f >> buffer;
// What if *only* now the eof / fail bit is set?
/* Use `buffer` */
}
あなたは正しく書くことができます:
while (!f.eof())
{
f >> buffer >> std::ws;
if (f.fail())
break;
/* Use `buffer` */
}
しかし
while (f >> buffer)
{
/* Use `buffer` */
}
シンプルでエラーの発生が少ない
さらなる参考文献:
-
std::ws
:入力ストリームから先行する空白を破棄する -
std::basic_ios::fail
:関連するストリームでエラーが発生した場合はtrue
返しtrue
非標準のロケール設定でファイルを書き込む
異なるロケール設定を使用してデフォルトにファイルを書き込む必要がある場合は、 std::locale
およびstd::basic_ios::imbue()
を使用して特定のファイルストリームに対して行うことができます。
使用の手引き:
- ファイルを開く前に、常にストリームにローカルを適用する必要があります。
- ストリームに画像が埋め込まれたら、ロケールを変更しないでください。
制限の理由:現在のロケールが状態に依存していないか、ファイルの先頭を指していない場合、ロケールを含むファイルストリームの埋め込みは未定義の動作をします。
UTF-8ストリーム(およびその他のストリーム)は状態に依存しません。また、UTF-8ロケールを含むファイルストリームは、ファイルが開かれたときにBOMマーカーをファイルから読み込み、読み取ることがあります。ファイルを開くだけでファイルから文字が読み込まれ、先頭には表示されません。
#include <iostream>
#include <fstream>
#include <locale>
int main()
{
std::cout << "User-preferred locale setting is "
<< std::locale("").name().c_str() << std::endl;
// Write a floating-point value using the user's preferred locale.
std::ofstream ofs1;
ofs1.imbue(std::locale(""));
ofs1.open("file1.txt");
ofs1 << 78123.456 << std::endl;
// Use a specific locale (names are system-dependent)
std::ofstream ofs2;
ofs2.imbue(std::locale("en_US.UTF-8"));
ofs2.open("file2.txt");
ofs2 << 78123.456 << std::endl;
// Switch to the classic "C" locale
std::ofstream ofs3;
ofs3.imbue(std::locale::classic());
ofs3.open("file3.txt");
ofs3 << 78123.456 << std::endl;
}
クラシック "C"ロケールに明示的に切り替えることは、プログラムが別のデフォルトロケールを使用し、ファイルの読み書きに固定された標準を確保したい場合に便利です。 "C"優先ロケールを使用すると、この例では
78,123.456
78,123.456
78123.456
たとえば、優先ロケールがドイツ語で、したがって異なる数値書式を使用する場合、この例では、
78 123,456
78,123.456
78123.456
(最初の行の小数点のカンマに注意してください)。