Menu

C++のgetline:1行全体の読み込み、入力スキップの解決、ファイル読み込み

std::getline で1行全体を std::string に読み込む方法。cin との使い方、cin >> のあとに空文字列が返る理由、cin.ignore と std::ws による解決策、独自の区切り文字、ファイルを終わりまで1行ずつ読む方法を解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

1行全体を読み込む

cin >> text が読み込むのは1語だけで、最初のスペースで止まります。ユーザーが Enter キーまでに入力したすべてをスペースも含めて読み込むには、<string> ヘッダーの std::getline を使います。

#include <iostream>
#include <string>

int main() {
    std::string name;
    std::cout << "Full name: ";
    std::getline(std::cin, name);
    std::cout << "Hello, " << name << "!\n";
}

Ada Lovelace と入力すると Hello, Ada Lovelace! と表示されます。std::cin >> name だと Hello, Ada! と表示され、Lovelace は入力に残ったままになります。

シグネチャは次のとおりです。

std::istream& std::getline(std::istream& input, std::string& text);
std::istream& std::getline(std::istream& input, std::string& text, char delim);
  • input は任意の入力ストリームです。std::cin、std::ifstream で開いたファイル、文字列から作った std::istringstream などが使えます。
  • text は読み込んだ文字で置き換えられます(追記ではありません)。
  • delim は読み込みを終える文字です。デフォルトは '\n' です。区切り文字はストリームから取り除かれ、text には格納されません。
  • 戻り値はストリームそのもので、そのおかげで条件式の中で判定できます。

getline はどの入力ストリームでも同じように動作するので、このページの実行できる例は、固定のテキストを持つ std::istringstream から読み込んでいます。何も入力しなくても出力が予測できるからです。キーボードから読み込むには、ストリームの変数を std::cin に置き換えてください。

出力:

[Ada Lovelace]
[Alan Turing]

角カッコを見ると、改行がどちらの文字列にも含まれていないことがわかります。

cin >> のあとの getline が空文字列を返す

これは getline でいちばんよくある問題です。>> で数値を読み、次に getline で1行を読むと、getline が飛ばされたように見えます。

出力:

age: 30
city: []

input >> age は文字 30 を読み込み、数値の一部になれない最初の文字、つまり改行で止まります。その改行はストリームに残ります。次に getline は次の改行まで読み込もうとしてすぐにそれを見つけ、空文字列を返します。std::cin の場合、プログラムが2つ目の答えを待たずに進んでしまうという形で現れます。

標準的な直し方は2つあります。どちらも >> と getline の間に入れます。

直し方1:ignore で行の残りを捨てる。

出力:

age: 30
city: [Paris]

ignore(n, '\n') は、改行を1つ取り除くか n 文字を取り除くまで、どちらか早いほうまで文字を捨てます。std::streamsize の最大値を渡すと「どれだけ先でも改行まで」という意味になります。数値のあとに同じ行で入力されたもの(末尾のスペースなど)も取り除かれます。キーボードの場合は std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); と書き、#include <limits> が必要です。

引数なしの std::cin.ignore() は、ちょうど1文字だけ捨てます。数値の直後に改行がある場合は動きますが、その前にスペースがあると失敗します。

直し方2:std::ws で先頭の空白を読み飛ばす。

出力:

age: 30
city: [Paris]

std::ws は、現在位置にある空白(スペース、タブ、改行)をすべて読み捨てるマニピュレーターです。input >> std::ws はストリームを返すので、そのまま getline に渡せます。空行や先頭のスペースも読み飛ばすので、名前や都市名には便利ですが、先頭のスペースや空行に意味がある入力には向きません。その場合は ignore を使いましょう。

入力の終わりまで読み込む

getline はストリームを返し、ストリームは読み込みが失敗すると false に変換されます。読み込む行がなくなると getline は失敗するので、それを while ループで囲めば、すべての行を読み込んで最後で止まります。

出力:

1: [apples 3]
2: [bread 1]
3: []
4: [milk 2]
read 4 lines

この出力からわかることが2つあります。

  • 空行も読み込みとしては成功です。空文字列が得られ、ループは続きます。ループを止めるのは入力の終わりに達したときだけです。
  • 最後の行に末尾の改行は必要ありません。テキストが \n なしの milk 2 で終わっていても、getline はその行を返し、次の呼び出しで失敗します。

std::cin の場合、同じループは入力が終わるまで読み込みます。入力が終わるのは、ファイルからパイプで入力したとき(./program < data.txt)や、ユーザーが macOS と Linux で Ctrl+D を押したとき、Windows で Ctrl+Z のあとに Enter を押したときです。

while (!input.eof()) { std::getline(input, line); ... } というパターンは避けましょう。eof() は、読み込みがすでに終わりに達したあとで初めて true になるので、本体が1回余分に実行され、読み込みは失敗し、line には古い値が残ります。代わりに getline の呼び出しを条件式に入れましょう。

ファイルを1行ずつ読み込む

ファイルでも同じループを使います。<fstream> の std::ifstream でファイルを開き、開けたことを確認してから読み込みます。

出力:

1. Ada 92
2. Grace 88
3. Alan 95

if (!file) は、ループの前にファイルがない場合やパスが間違っている場合を捕まえます。これがないと、ループは単に0行を読み込むだけで、エラーではなく空のファイルのように見えてしまいます。ファイルは、main の終わりで file がスコープを抜けるときに自動で閉じられます。

Windows で保存したファイルは、各行が \r\n で終わります。そのようなファイルを Linux や macOS で読み込むと、getline は \n を取り除きますが \r は残すので、すべての行の末尾に目に見えないキャリッジリターンが付き、line == "done" のような比較がうまくいきません。読み込んだあとに取り除きましょう。

if (!line.empty() && line.back() == '\r') {
    line.pop_back();
}

独自の区切り文字を使う

3番目の引数で、読み込みを終える文字を変えられます。よくある使い方は、1行をカンマで分割することです。まず1行を読み込み、それを std::istringstream を通して分割します。

出力:

fields: 3
[Ada Lovelace]
[36]
[London]

デフォルトの形式で改行がそうであるのと同じように、カンマはストリームから取り除かれ、格納されません。独自の区切り文字を使うと、改行はふつうの文字になることに注意してください。複数行のファイルに対して直接 std::getline(file, field, ',') を呼ぶと、ある行の最後のフィールドと次の行の最初のフィールドが、"London\nGrace Hopper" のようにくっついて返ってきます。1行ずつ読み込んで、各行を個別に分割すればこれを避けられます。std::istringstream については、"36" のようなフィールドを数値に変換する方法も含めて 文字列ストリームのページ で詳しく解説しています。

std::getline と cin.getline の違い

似た名前のメンバー関数もあります。

char buffer[20];
std::cin.getline(buffer, 20);        // メンバー関数、固定サイズの char 配列

std::string text;
std::getline(std::cin, text);        // 非メンバー関数、std::string

cin.getline は char 配列に書き込み、最大で size - 1 文字と終端の '\0' を格納します。それより長い行はストリームを失敗状態にし、std::cin.clear() を呼ぶまで以降の読み込みはすべて飛ばされます。std::string を使う std::getline なら、長さの上限を気にする必要はありません。char 配列を要求する API を扱う場合を除き、こちらを使いましょう。

よくある間違い

  • 改行を片付けずに >> と getline を混ぜる。 両者の間に ignore(...) を入れるか、std::ws を使いましょう。std::cin での同じ問題と、数値の読み込みに失敗したときの立て直し方は 入力と出力のページ で紹介しています。
  • int に対して getline(std::cin, number) を呼ぶ。 getline が読み込めるのは std::string だけです。1行を読み込んでから、std::stoi や std::istringstream で変換しましょう。
  • eof() でループする。 getline の呼び出しそのものを判定しましょう:while (std::getline(input, line))。
  • #include <string> を忘れる。 文字列用の std::getline は <string> で宣言されています。コンパイラによっては <iostream> 経由で通ってしまい、別のコンパイラでコードが失敗します。
  • ファイルが開けたかを確認しない。 パスが間違っていると std::ifstream は何も言わずに失敗し、読み込みループは0回しか実行されません。

よくある質問

C++の getline は何をしますか?

std::getline(stream, text) は、stream から改行に達するまで文字を読み込んで std::string の text に格納し、その改行は格納せずにストリームから取り除きます。cin >> text と違ってスペースも残るので、フルネームや文章のような1行全体を読み込めます。

C++で cin >> のあとの getline が飛ばされるのはなぜですか?

cin >> number は数値のあとに入力した改行で読み込みを止め、その改行を入力バッファに残します。次の getline はすぐにその改行を見つけ、空文字列を返します。先に std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); で捨てるか、std::getline(std::cin >> std::ws, text) で先頭の空白を読み飛ばしましょう。

改行以外の区切り文字で getline を使うには?

区切り文字を3番目の引数に渡します:std::getline(stream, field, ',')。次のカンマまでを読み込み、その前のテキストを格納して、カンマは捨てます。この場合、改行はふつうの文字として扱われるので、1行から作った std::stringstream と組み合わせて使うのが一般的です。

C++でファイルを1行ずつ読み込むには?

std::ifstream file("data.txt"); で開き、while (std::getline(file, line)) { ... } でループします。getline はストリームを返し、読み込む行がなくなるとそれが false に変換されるので、ループはファイルの終わりで止まります。

cin.getline と getline の違いは?

std::getline(std::cin, text) は std::string に読み込む非メンバー関数で、文字列はどんな長さの行にも合わせて伸びます。std::cin.getline(buffer, size) は固定長の char 配列に読み込むメンバー関数で、行が size - 1 文字より長いと失敗します。生の配列を使う理由がない限り、std::string と std::getline を使いましょう。

Coddyのプログラミング言語のイラスト

Coddyでコードを学ぼう

始める