C++ ファイルとストリーム
C++ ファイルとストリーム
ここまでは、 標準 入力からストリームを読み取るためのcin メソッド と 標準出力にストリームを書き込むためのcoutメソッドを提供するiostream標準ライブラリを使用してきました 。
このチュートリアルでは、ストリームをファイルから読み取り、ストリームをファイルに書き込む方法を示します。これには、C++ で別の標準ライブラリfstreamを使用する必要があり 、次の 3 つの新しいデータ型が定義されています。
データの種類 | 説明 |
---|---|
オフストリーム | このデータ型は出力ファイル ストリームを表し、情報を作成してファイルに書き込むために使用されます。 |
イフストリーム | このデータ型は入力ファイル ストリームを表し、ファイルから情報を読み取るために使用されます。 |
fストリーム | 通常、このデータ型はファイル ストリームを表し、ofstream 関数と ifstream 関数の両方を備えています。つまり、ファイルの作成、ファイルへの情報の書き込み、およびファイルからの情報の読み取りが可能です。 |
C++ でファイルを処理するには、ヘッダー ファイル <iostream> および <fstream> を C++ ソース コード ファイルに含める必要があります。
ファイルを開く
ファイルから情報を読み書きする前に、ファイルを開く必要があります。ofstream オブジェクト と fstream オブジェクトの両方を使用して、ファイルを書き込み用に開くことができます。ファイルを読み取り用に開くだけでよい場合は、 ifstream オブジェクトを使用します。
以下は、fstream、ifstream、および ofstream オブジェクトのメンバーである open() 関数の標準構文です。
void open(const char *filename, ios::openmode モード);
ここで、open() メンバー関数の最初のパラメーターは、開くファイルの名前と場所を指定し、2 番目のパラメーターは、ファイルを開くモードを定義します。
モードフラグ | 説明 |
---|---|
ios::アプリ | 追加モード。すべての書き込みはファイルの末尾に追加されます。 |
ios::ate | ファイルを開いた後、ファイルの末尾に位置付けます。 |
ios::in | ファイルを読み取り用に開きます。 |
ios::アウト | ファイルを書き込み用に開きます。 |
ios::トランク | ファイルが既に存在する場合、ファイルを開く前にその内容が切り捨てられます。つまり、ファイルの長さは 0 に設定されます。 |
上記のモードを 2 つ以上組み合わせることができます。たとえば、ファイルを書き込みモードで開き、ファイルが既に存在する場合にファイルを切り捨てたい場合は、次の構文を使用できます。
ストリーム出力ファイル; outfile.open("file.dat", ios::out | ios::trunc );
同様に、読み取りおよび書き込み用にファイルを開きたい場合は、次の構文を使用できます。
fstream afile; afile.open("file.dat", ios::out | ios::in );
ファイルを閉じる
C++ プログラムが終了すると、すべてのストリームを自動的に閉じてフラッシュし、割り当てられたすべてのメモリを解放し、開いているすべてのファイルを閉じます。ただし、プログラマーは、プログラムが終了する前に、開いているすべてのファイルを閉じるという良い習慣を身に付ける必要があります。
以下は、fstream、ifstream、および ofstream オブジェクトのメンバーである close() 関数の標準構文です。
ボイドクローズ();
ファイルに書き込む
C++ プログラミングでは、ストリーム挿入演算子 ( << ) を使用して情報をファイルに書き込みます。これは、この演算子を使用して情報を画面に出力するのと同じです。唯一の違いは、ここではcoutオブジェクト の代わりに ofstream または fstreamオブジェクトを使用していることです 。
ファイルを読む
C++ プログラミングでは、キーボードから情報を入力するのと同じように、ストリーム抽出演算子 ( >> ) を使用してファイルから情報を読み取ります。唯一の違いは、ここではcinオブジェクト の代わりに ifstream または fstreamオブジェクトを使用していることです 。
インスタンスの読み取りと書き込み
次の C++ プログラムは、読み取り/書き込みモードでファイルを開きます。ユーザーが入力した情報をファイル afile.dat に書き込んだ後、プログラムはファイルから情報を読み取り、画面に出力します。
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
char data[100];
// 以写模式打开文件
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// 向文件写入用户输入的数据
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// 再次向文件写入用户输入的数据
outfile << data << endl;
// 关闭打开的文件
outfile.close();
// 以读模式打开文件
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// 在屏幕上写入数据
cout << data << endl;
// 再次从文件读取数据,并显示它
infile >> data;
cout << data << endl;
// 关闭打开的文件
infile.close();
return 0;
}
上記のコードをコンパイルして実行すると、次の入力と出力が生成されます。
$./a.out ファイルへの書き込み 名前を入力してください: Zara 年齢を入力してください: 9 ファイルから読み取る Zara 9
上記の例では、外部から行を読み取る getline() 関数など、cin オブジェクトの追加関数が使用され、ignore() 関数は、前の read ステートメントによって残された冗長な文字を無視します。
ファイルの場所のポインタ
istream と ostream はどちらも 、ファイルの場所のポインターを再配置するためのメンバー関数を提供します。これらのメンバー関数には、 istream のseekg (「seek get」) とostream の seekp (「seek put」)が含まれます。
通常、seekg と seekp の引数は長整数です。2 番目のパラメーターを使用して、検索方向を指定できます。検索方向は、 ios::beg (デフォルト、ストリームの先頭から位置付けを開始)、 ios::cur (ストリームの現在の位置から位置付けを開始)、または ios::end (ストリームの末尾から開始)のいずれかです。ストリーム位置)。
ファイル位置ポインターは、ファイルの先頭からポインターの位置までのバイト数を指定する整数値です。「取得」ファイルの場所のポインターを見つける例を次に示します。
// fileObject の n バイト目に位置付けます (ios::beg と仮定) fileObject.seekg( n ); // fileObject の現在の位置から n バイトだけファイルの読み取りポインタを戻します fileObject.seekg ( n, ios::cur ); // ファイルの読み取りポインタを fileObject の末尾から n バイト戻します fileObject.seekg( n, ios::end ); // fileObject の末尾に移動します fileObject.seekg( 0, ios: :end );
C++ 例外処理
C++ 例外処理
例外は、プログラムの実行中に発生する問題です。C++ 例外は、ゼロ除算の試みなど、プログラムの実行中に発生する特殊な状態です。
例外は、プログラム制御を転送する方法を提供します。C++ の例外処理には、try、catch、throwの 3 つのキーワードが含まれます。
- throw: 問題が発生すると、プログラムは例外をスローします。これは、 throwキーワードを使用して 行われます。
- catch: 問題を処理したい場合は、例外ハンドラーを介して例外をキャッチします。例外をキャッチするには、 catch キーワードを使用します。
- try: try ブロック内のコードは、アクティブ化される特定の例外を識別します。通常、この後に 1 つ以上の catch ブロックが続きます。
ブロックが例外をスローした場合、例外をキャッチするメソッドは try および catch キーワードを使用します。例外をスローする可能性のあるコードは try ブロックに配置され、try ブロック内のコードは保護されたコードと呼ばれます。try/catch ステートメントを使用するための構文は次のとおりです。
try { // 保護されたコード } catch( ExceptionName e1 ) { // キャッチ ブロック } catch( ExceptionName e2 ) { // キャッチ ブロック } catch( ExceptionName eN ) { // キャッチ ブロック }
tryブロックがさまざまな状況でさまざまな例外をスローする場合は 、複数のcatchステートメント をリストして、さまざまな種類の例外をキャッチすることを試みることができます 。
例外をスローする
throwステートメントを使用して、 コード ブロック内の任意の場所で例外をスローできます 。throw ステートメントのオペランドは任意の式にすることができ、式の結果の型によって、スローされる例外の型が決まります。
ゼロで除算しようとすると例外がスローされる例を次に示します。
double division(int a, int b) { if( b == 0 ) { throw "ゼロ除算条件!"; 戻り ます (a/b); }
例外をキャッチ
catch ブロックは try ブロックの後に続き、例外をキャッチします。キャッチする例外のタイプを指定できます。これは、catch キーワードの後の括弧内の例外宣言によって決定されます。
try { // 保護コード }catch( ExceptionName e ) { // ExceptionName 例外を処理するコード }
上記のコードは、タイプExceptionNameの例外をキャッチします 。try ブロックによってスローされた任意のタイプの例外を catch ブロックで処理できるようにする場合は、次のように、例外宣言の括弧内で省略記号 ... を使用する必要があります。
try { // ガード コード } catch(...) { // あらゆる例外を処理できるコード }
以下は、ゼロによる除算の例外がスローされ、catch ブロックでキャッチされる例です。
#include <iostream>
using namespace std;
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}
int main ()
{
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}
由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。当上面的代码被编译和执行时,它会产生下列结果:
Division by zero condition!
C++ 标准的异常
C++ 提供了一系列标准的异常,定义在 <exception> 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:
下表是对上面层次结构中出现的每个异常的说明:
异常 | 描述 |
---|---|
std::exception | 该异常是所有标准 C++ 异常的父类。 |
std::bad_alloc | 该异常可以通过 new 抛出。 |
std::bad_cast | 该异常可以通过 dynamic_cast 抛出。 |
std::bad_exception | 这在处理 C++ 程序中无法预期的异常时非常有用。 |
std::bad_typeid | 该异常可以通过 typeid 抛出。 |
std::logic_error | 理论上可以通过读取代码来检测到的异常。 |
std::domain_error | 当使用了一个无效的数学域时,会抛出该异常。 |
std::invalid_argument | 当使用了无效的参数时,会抛出该异常。 |
std::length_error | 当创建了太长的 std::string 时,会抛出该异常。 |
std::out_of_range | 该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator[]()。 |
std::runtime_error | 理论上不可以通过读取代码来检测到的异常。 |
std::overflow_error | この例外は、数学的なオーバーフローが発生したときにスローされます。 |
std::range_error | この例外は、範囲外の値を格納しようとするとスローされます。 |
std::underflow_error | この例外は、数学的なアンダーフローが発生したときにスローされます。 |
新しい例外を定義する
例外クラスを継承してオーバーロードすることにより、 新しい例外を定義できます 。次の例は、std::exception クラスを使用して独自の例外を実装する方法を示しています。
#include <iostream>
#include <exception>
using namespace std;
struct MyException : public exception
{
const char * what () const throw ()
{
return "C++ Exception";
}
};
int main()
{
try
{
throw MyException();
}
catch(MyException& e)
{
std::cout << "MyException caught" << std::endl;
std::cout << e.what() << std::endl;
}
catch(std::exception& e)
{
//其他的错误
}
}
これにより、次の結果が生成されます。
MyException が C++ 例外をキャッチしました
ここで、what() は例外クラスによって提供される public メソッドであり、すべての子例外クラスによってオーバーライドされています。これにより、例外の理由が返されます。