Qtファイル操作の決定版:readLineInto()と他のメソッドを使いこなす

2025-05-27

QTextStream::readLineInto() とは

QTextStream は、Qt フレームワークでテキストデータを読み書きするための便利なクラスです。ファイル、QByteArray、または QString からデータを読み書きするのに使われます。

readLineInto() メソッドは、QTextStream から1行分のテキストを読み込み、指定された QString オブジェクトにその内容を格納します。

構文

bool QTextStream::readLineInto(QString *line, qint64 maxlen = 0)

引数

  • qint64 maxlen = 0: 読み込む最大文字数を指定します。0 (デフォルト値) の場合、行全体が読み込まれます。
  • QString *line: 読み込んだ行のテキストが格納される QString オブジェクトへのポインタです。このメソッドは、新しい QString を作成するのではなく、既存の QString オブジェクトを再利用します。

戻り値

  • ストリームの終わりに到達した場合や、エラーが発生した場合は false を返します。
  • 行の読み込みに成功した場合、true を返します。

QTextStream には readLine() という似たようなメソッドもありますが、readLineInto() は特にパフォーマンスが重視される場合に推奨されます。

readLine() との違い

  • readLineInto(): 既存の QString オブジェクト(引数として渡したもの)に直接データを書き込みます。これにより、QString オブジェクトの頻繁な再割り当てが避けられ、特に大きなファイルや多くの行を処理する場合にパフォーマンスが向上します。
  • readLine(): 新しい QString オブジェクトを返します。ループ内で繰り返し呼び出すと、新しい QString オブジェクトが頻繁に作成され、メモリの割り当てと解放が繰り返されるため、オーバーヘッドが発生する可能性があります。

使用例

一般的なファイルから行を読み込むループの例を以下に示します。

#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("my_text_file.txt"); // 読み込むファイル名を指定

    // ファイルを開く
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qCritical() << "ファイルを開けませんでした。";
        return 1;
    }

    QTextStream in(&file);
    QString line; // 読み込んだ行を格納するQStringオブジェクト

    // ファイルの終わりまで行を読み込む
    while (in.readLineInto(&line)) {
        qDebug() << "読み込んだ行:" << line;
    }

    file.close(); // ファイルを閉じる

    return 0;
}

この例では、my_text_file.txt という名前のファイルから各行を読み込み、その内容をデバッグ出力しています。while (in.readLineInto(&line)) のループが、ストリームの終わりに到達するまで効率的に行を読み込み続けます。



ファイルが開かれていない、または読み取りモードではない

これは最も基本的なエラーです。QTextStreamが関連付けられているQIODevice(通常はQFile)が正しく開かれていない場合、readLineInto()は常にfalseを返します。

エラーの症状

  • QFile::errorString()が「No such file or directory」や「Permission denied」などのエラーメッセージを返す。
  • ファイルの内容が読み込まれない。
  • readLineInto()が常にfalseを返す。

原因

  • QIODevice::ReadOnlyフラグを指定せずにファイルを開いている。
  • 読み取り権限がない。
  • ファイルパスが間違っている。
  • QFile::open()が失敗している。

トラブルシューティング

  • QIODevice::Textフラグを使用する
    テキストモードで開くことで、適切なエンコーディング処理が行われます。
  • 権限を確認する
    ファイルに対する読み取り権限があることを確認します。
  • ファイルパスを検証する
    ファイルパスが正しいか、ファイルが存在するかを確認します。相対パスを使用している場合は、カレントディレクトリが正しいかどうかも確認してください。
  • QFile::open()の戻り値を確認する
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) のように、必ずopen()メソッドの戻り値を確認してください。


QFile file("non_existent_file.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    qWarning() << "ファイルを開けませんでした:" << file.errorString(); // ここでエラーメッセージを確認
    return;
}
QTextStream in(&file);
QString line;
while (in.readLineInto(&line)) {
    // このループは実行されない
}
file.close();

QTextStreamの再構築をループ内で繰り返し行っている

これはパフォーマンスの問題だけでなく、読み取り位置のずれを引き起こす可能性のある、見落としがちなエラーです。

エラーの症状

  • メモリ使用量が増大する(readLine()の場合により顕著)。
  • readLineInto()が予期せずfalseを返す。
  • ファイルの一部しか読み込まれない、または同じ行が繰り返し読み込まれる。

原因

  • QTextStreamは内部的にバッファを持っており、一度に多くのデータを基になるQIODeviceから読み込むことがあります。ループ内でQTextStreamオブジェクトを繰り返し作成すると、そのたびに新しいバッファが作成され、前のQTextStreamが読み込んだが処理しきれなかったデータが破棄されるため、ファイルポインタが意図しない位置に進んでしまいます。

トラブルシューティング

  • QTextStreamオブジェクトはループの外で一度だけ作成する
    これが最も重要なポイントです。

悪い例

QFile file("my_text_file.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QString line;
    while (true) {
        QTextStream in(&file); // ループ内でQTextStreamを繰り返し作成している!
        if (in.readLineInto(&line) == false)
            break;
        qDebug() << line;
    }
    file.close();
}

正しい例

QFile file("my_text_file.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QTextStream in(&file); // ループの外で一度だけ作成
    QString line;
    while (in.readLineInto(&line)) {
        qDebug() << line;
    }
    file.close();
}

異なるQIODeviceアクセス方法との混在

QTextStreamを使用しているQIODeviceに対して、QFile::read()QFile::readLine()など、QTextStream以外のメソッドで直接読み取りを行うと、QTextStreamの内部バッファとQIODeviceのファイルポインタが同期されなくなり、予期せぬ読み取り結果となります。

エラーの症状

  • 不完全な行や意味不明なデータが読み込まれる。
  • readLineInto()が読み込むべき行をスキップする。

原因

トラブルシューティング

  • 一度QTextStreamを使用し始めたら、そのデバイスからの読み取りは常にそのQTextStreamインスタンスを通して行う。


QFile file("my_text_file.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QTextStream in(&file);
    QString line1;
    in.readLineInto(&line1); // QTextStreamで1行目を読む

    QByteArray rawData = file.readLine(); // 直接QFileから読む(これは問題を引き起こす)

    QString line2;
    in.readLineInto(&line2); // QTextStreamの内部状態が狂っている可能性がある
    qDebug() << "Line 1:" << line1;
    qDebug() << "Raw Data:" << rawData;
    qDebug() << "Line 2:" << line2; // 期待通りの結果にならない可能性が高い
    file.close();
}

エンコーディングの問題

エラーの症状

  • ファイルの内容が正しく解析できない。

原因

  • ファイルのエンコーディングとQTextStreamが使用しているエンコーディングが一致しない。

トラブルシューティング

  • setEncoding()で明示的にエンコーディングを設定する
    ファイルのエンコーディングが分かっている場合は、setEncoding()を呼び出して設定します。


QFile file("japanese_text.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QTextStream in(&file);
    in.setEncoding(QStringConverter::Utf8); // UTF-8エンコーディングのファイルを読み込む場合

    // あるいは、Shift-JISの場合
    // in.setEncoding(QStringConverter::ShiftJis); 

    QString line;
    while (in.readLineInto(&line)) {
        qDebug() << "読み込んだ行:" << line;
    }
    file.close();
}

ストリームのステータスを確認しない

readLineInto()falseを返した場合、それがファイルの終端によるものか、あるいは読み取りエラーによるものかを区別するために、QTextStream::status()を使用できます。

エラーの症状

  • readLineInto()falseを返した際に、具体的な原因が分からない。

原因

  • エラーハンドリングが不十分。

トラブルシューティング

  • QTextStream::status()でストリームの状態をチェックする


QFile file("my_text_file.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QTextStream in(&file);
    QString line;
    while (in.readLineInto(&line)) {
        qDebug() << "読み込んだ行:" << line;
    }

    if (in.status() == QTextStream::ReadPastEnd) {
        qDebug() << "ファイルの終端に到達しました。";
    } else if (in.status() == QTextStream::ReadCorruptData) {
        qWarning() << "破損したデータを読み込みました。";
    } else if (in.status() != QTextStream::Ok) {
        qWarning() << "その他の読み取りエラーが発生しました。";
    }
    file.close();
}

QTextStream::readLineInto()を効果的かつ問題なく使用するためには、以下の点に注意することが重要です。

  • readLineInto()falseを返した場合、QTextStream::status()で原因を確認する。
  • テキストファイルのエンコーディングを正しく設定する。
  • QTextStreamQIODeviceの読み取り方法を混在させない。
  • QTextStreamオブジェクトはループの外で一度だけ作成する。
  • ファイルが正しく開かれているか常に確認する。


QTextStream::readLineInto()は、既存のQStringオブジェクトに読み込んだ行を直接格納するため、メモリの再割り当てを最小限に抑え、パフォーマンスが向上します。特に大きなファイルや多くの行を処理する場合に効果的です。

基本的なファイルからの行読み込み

この例では、my_text_file.txtという名前のファイルから各行を読み込み、コンソールに出力します。

ファイルの内容 (my_text_file.txt)

これは1行目のテキストです。
2行目には数字も含まれます: 12345
そして最後の行です。

C++ コード

#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDebug> // デバッグ出力用

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 読み込むファイル名を指定
    QFile file("my_text_file.txt"); 

    // ファイルを開く
    // QIODevice::ReadOnly: 読み取り専用で開く
    // QIODevice::Text: テキストモードで開く(改行コードの自動変換など)
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "ファイルを開けませんでした:" << file.errorString();
        return 1; // エラー終了
    }

    // QTextStreamオブジェクトを作成し、QFileに関連付ける
    QTextStream in(&file);

    // 読み込んだ行を格納するQStringオブジェクト
    QString line; 

    // ファイルの終わりに到達するまで、行を読み込む
    // readLineInto()は行の読み込みに成功したらtrueを返す
    while (in.readLineInto(&line)) {
        qDebug() << "読み込んだ行:" << line;
    }

    // ファイルを閉じる(QFileがスコープを抜けるときにも自動的に閉じられるが、明示的に閉じるのが良い習慣)
    file.close();

    qDebug() << "ファイルの読み込みが完了しました。";

    return 0; // 正常終了
}

解説

  • qDebug() << "読み込んだ行:" << line;:読み込んだ行の内容をコンソールに出力します。
  • while (in.readLineInto(&line)):このループがファイルの読み取りの中核です。
    • in.readLineInto(&line)QTextStreamから1行分のテキストを読み込み、その内容をline変数に格納します。
    • 読み込みが成功した場合(ファイルの終端に達していない、エラーがないなど)はtrueを返し、ループが続行されます。
    • 読み込みが失敗した場合(ファイルの終端に達した、読み取りエラーが発生したなど)はfalseを返し、ループが終了します。
  • QString line;:読み込んだ行のテキストを格納するためのQString変数を宣言します。この変数はループの外で一度だけ宣言され、readLineInto()が呼び出されるたびにその内容が上書きされます。
  • QTextStream in(&file);:開いたQFileオブジェクトを引数に渡し、QTextStreamオブジェクトを作成します。これにより、QTextStreamを通してファイルの内容にアクセスできるようになります。
  • file.open(QIODevice::ReadOnly | QIODevice::Text):ファイルを読み取り専用のテキストモードで開きます。ファイルのオープンに失敗した場合(ファイルが存在しない、権限がないなど)、falseを返すため、エラー処理が必要です。
  • QFile file("my_text_file.txt");QFileオブジェクトを作成し、読み込むファイルのパスを指定します。

エンコーディングを指定してファイルを読み込む

こんにちは、Qtの世界へようこそ!
これは日本語のテキストファイルです。

C++ コード

#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QStringConverter> // エンコーディング指定用

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("japanese_text.txt");

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "ファイルを開けませんでした:" << file.errorString();
        return 1;
    }

    QTextStream in(&file);
    // ファイルのエンコーディングがUTF-8であると指定
    // Qt 6以降ではQStringConverter::Encodingを使用
    // Qt 5以前ではQTextCodec::codecForName("UTF-8")を使用
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    in.setEncoding(QStringConverter::Utf8);
#else
    in.setCodec(QTextCodec::codecForName("UTF-8"));
#endif

    QString line;
    while (in.readLineInto(&line)) {
        qDebug() << "読み込んだ行 (UTF-8):" << line;
    }

    file.close();

    qDebug() << "日本語ファイルの読み込みが完了しました。";

    return 0;
}

解説

  • in.setEncoding(QStringConverter::Utf8); (Qt 6) または in.setCodec(QTextCodec::codecForName("UTF-8")); (Qt 5) :QTextStreamのエンコーディングをファイルの実際のエンコーディングに合わせて設定します。これにより、文字化けを防ぎ、テキストが正しく読み込まれるようになります。

最大文字数を指定して行を読み込む

readLineInto()のオプション引数maxlenを使用して、読み込む文字数を制限することができます。

ファイルの内容 (long_lines.txt)

これは非常に長い行のテキストです。
この行もかなり長いです。

C++ コード

#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("long_lines.txt");

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "ファイルを開けませんでした:" << file.errorString();
        return 1;
    }

    QTextStream in(&file);
    QString line;
    qint64 maxLength = 10; // 読み込む最大文字数を10に設定

    qDebug() << "最大" << maxLength << "文字で各行を読み込みます:";
    while (in.readLineInto(&line, maxLength)) {
        qDebug() << "読み込んだ部分:" << line;
    }

    // 読み込みが終端まで行われたか、エラーがあったかを確認
    if (in.status() == QTextStream::ReadPastEnd) {
        qDebug() << "ファイルの終端に到達しました。";
    } else if (in.status() != QTextStream::Ok) {
        qWarning() << "読み取り中にエラーが発生しました。";
    }

    file.close();

    return 0;
}
  • in.status():ループ終了後、ストリームの状態を確認することで、読み込みがファイルの終端によるものか、それとも他のエラーによるものかを判断できます。
  • while (in.readLineInto(&line, maxLength))maxLength10を指定しているので、各行から最大10文字までしか読み込まれません。行が10文字より長い場合でも、読み込まれるのは最初の10文字のみです。


QTextStream::readLine()

readLineInto()と同様にQTextStreamのメソッドですが、こちらは読み込んだ行を新しいQStringオブジェクトとして返します。

特徴

  • NULL文字列のチェック
    ファイルの終端に達すると、isNull()trueを返すQStringオブジェクトを返します。
  • メモリのオーバーヘッド
    ループ内で繰り返し呼び出すと、新しいQStringオブジェクトが生成され続けるため、メモリの割り当てと解放が頻繁に発生し、パフォーマンスに影響を与える可能性があります。特に大きなファイルや多くの行を処理する場合に顕著です。
  • 簡潔なコード
    読み込んだ行を直接戻り値として受け取れるため、コードがより簡潔になります。

使用例

#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("my_text_file.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "ファイルを開けませんでした:" << file.errorString();
        return 1;
    }

    QTextStream in(&file);
    QString line;

    // ファイルの終端に到達するか、無効な文字列(isNull()がtrue)が返されるまで読み込む
    while (!(line = in.readLine()).isNull()) {
        qDebug() << "読み込んだ行:" << line;
    }

    file.close();
    qDebug() << "ファイルの読み込みが完了しました。";

    return 0;
}

readLineInto()との比較

  • readLine()は新しいQStringを生成するため、パフォーマンスが重視される場合はreadLineInto()が推奨されます。しかし、ほとんどのアプリケーションではその差はごくわずかです。
  • readLineInto()は既存のQStringを再利用するため、ガベージコレクション(またはそれに近い概念)によるオーバーヘッドが少ないです。

QFile::readLine()

QFileクラス自体が提供するメソッドで、ファイルから直接1行分のデータをQByteArrayとして読み込みます。

特徴

  • バイナリモードのファイルにも使える
    QIODevice::Textフラグなしで開いた場合、バイナリファイルからデータを読み込む際にも使えます。
  • 改行コードを含む
    読み込んだQByteArrayには、行末の改行コード(例: \n\r\n)が含まれます。これを処理する必要がある場合があります(例: trimmed()chop()で除去)。
  • QByteArrayで返される
    テキストとして扱う場合は、QStringに変換する必要があります。この変換時にエンコーディングを考慮する必要があります。
  • 直接的なファイル操作
    QTextStreamを介さずに、より低レベルでファイルから読み取ります。

使用例

#include <QCoreApplication>
#include <QFile>
#include <QDebug>
#include <QByteArray> // QByteArray用

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("my_text_file.txt");
    // テキストモードで開く(改行コードの自動変換が行われる)
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "ファイルを開けませんでした:" << file.errorString();
        return 1;
    }

    QByteArray lineBytes;

    // ファイルの終わりに到達するまで、行を読み込む
    // readLine()は読み込んだバイト配列を返す。終端では空のQByteArrayを返す
    while (!(lineBytes = file.readLine()).isEmpty()) {
        // QByteArrayをQStringに変換する。この際、ファイルのエンコーディングを考慮する
        // ここではUTF-8を想定
        QString line = QString::fromUtf8(lineBytes.trimmed()); // 改行コードを除去するためtrimmed()を使用

        qDebug() << "読み込んだ行 (QFile::readLine):" << line;
    }

    file.close();
    qDebug() << "ファイルの読み込みが完了しました。";

    return 0;
}

QTextStreamとの比較

  • パフォーマンスは一般的にQTextStreamの方が良いとされますが、非常に大きなファイルで特定のバイナリ処理が必要な場合はQFileの直接的なメソッドが選ばれることもあります。
  • QFile::readLine()は生のバイト列を返すため、エンコーディング変換は開発者が手動で行う必要があります。
  • QTextStreamは内部的にUnicode(UTF-16)バッファを使用し、エンコーディング変換を自動的に処理してくれます。

QTextStream::readAll() または QFile::readAll()

ファイル全体を一度に読み込む方法です。

特徴

  • 簡単な後処理
    読み込んだ後、QString::split()などを使って行に分割できます。
  • メモリ消費
    ファイルサイズが大きい場合、大量のメモリを消費する可能性があります。システムメモリに収まらないほどの巨大なファイルを読み込むのには適していません。
  • シンプル
    ファイル全体を1つのQStringQTextStream::readAll()の場合)またはQByteArrayQFile::readAll()の場合)に読み込みます。

使用例 (QTextStream::readAll())

#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("my_text_file.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "ファイルを開けませんでした:" << file.errorString();
        return 1;
    }

    QTextStream in(&file);
    QString allContent = in.readAll(); // ファイル全体をQStringとして読み込む

    file.close();

    qDebug() << "ファイル全体の内容:";
    qDebug() << allContent;

    // 必要に応じて行に分割
    QStringList lines = allContent.split('\n', Qt::SkipEmptyParts);
    qDebug() << "分割された行数:" << lines.size();
    for (const QString& line : lines) {
        qDebug() << "  - " << line.trimmed(); // 各行の前後にある空白や改行を削除
    }

    qDebug() << "ファイルの読み込みが完了しました。";

    return 0;
}

使用例 (QFile::readAll())

#include <QCoreApplication>
#include <QFile>
#include <QDebug>
#include <QByteArray>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("my_text_file.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { // Textモードで改行を正規化
        qWarning() << "ファイルを開けませんでした:" << file.errorString();
        return 1;
    }

    QByteArray allContentBytes = file.readAll(); // ファイル全体をQByteArrayとして読み込む

    file.close();

    // QByteArrayをQStringに変換
    QString allContent = QString::fromUtf8(allContentBytes);

    qDebug() << "ファイル全体の内容 (QFile::readAll):";
    qDebug() << allContent;

    qDebug() << "ファイルの読み込みが完了しました。";

    return 0;
}

readLineInto()との比較

  • readAll()は小~中規模のファイルには非常に便利ですが、大きなファイルではメモリ不足のリスクがあります。
  • readLineInto()はメモリ効率が良く、ファイルサイズに関わらず安定したパフォーマンスを提供します。

QDataStream (主にバイナリデータ用だが、テキストを扱うことも可能)

QDataStreamは主にQtの型をシリアライズ/デシリアライズするために設計されており、バイナリデータに適しています。テキストを扱うことも可能ですが、通常はQTextStreamの方が便利です。

特徴

  • テキストには不向き
    生のテキストを読み込むにはQTextStreamの方がはるかに簡単で、エンコーディング処理も自動で行われます。QDataStreamでテキストを読み込む場合、通常はQStringを書き込んだ時のバイナリフォーマットに従う必要があります。
  • バージョン管理
    ストリームのバージョンを設定できるため、異なるバージョンのアプリケーション間でデータの互換性を保つことができます。
  • バイナリデータのシリアライズ
    Qtのカスタム型を含む、構造化されたバイナリデータの読み書きに最適です。
#include <QCoreApplication>
#include <QFile>
#include <QDataStream>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 書き込み例(QDataStreamでQStringを書き込む)
    QFile outFile("data.bin");
    if (outFile.open(QIODevice::WriteOnly)) {
        QDataStream out(&outFile);
        out << QString("Hello from QDataStream!");
        out << QString("Second line of data.");
        outFile.close();
    }

    // 読み込み例(QDataStreamでQStringを読み込む)
    QFile inFile("data.bin");
    if (inFile.open(QIODevice::ReadOnly)) {
        QDataStream in(&inFile);
        QString str1, str2;
        in >> str1;
        in >> str2;
        qDebug() << "QDataStreamで読み込んだ文字列1:" << str1;
        qDebug() << "QDataStreamで読み込んだ文字列2:" << str2;
        inFile.close();
    }

    return 0;
}

QTextStreamとの比較

  • QDataStreamはアプリケーション間で効率的かつ安全にデータを交換するためのバイナリ形式に特化しています。テキストファイルを「行ごとに読み込む」という目的には通常使用されません。
  • QTextStreamは人間が読めるテキスト形式のデータに特化しています。

QTextStream::readLineInto()は、行ごとにテキストを効率的に読み込むための最も推奨される方法です。 しかし、以下のような場合は他の代替方法も考慮できます。

  • 低レベルなバイト操作やバイナリファイル
    QFile::readLine()QFile::read()QDataStreamが適しています。
  • ファイル全体を一度に処理したい場合
    QTextStream::readAll()QFile::readAll()が便利です。ただし、メモリ使用量に注意が必要です。
  • 簡単なケースや短いファイル
    QTextStream::readLine()がコードの簡潔さで勝ることがあります。