2

私はC++でのファイルとリソースの処理について学んだことを使おうとしていました:私は-のようdiffなユーティリティを書きたいと思います。

これが私の最新バージョンです

#include <iostream>
#include <cstdlib>
#include <fstream>

int main(int argc, char* argv[])
{
  if(argc!=3)
  {
    std::cout << "error: 2 arguments required, now exiting ..." << std::endl;
    exit (EXIT_FAILURE);
  }

  std::ifstream file_1(argv[1]);
  std::ifstream file_2(argv[2]);

  if( file_1.fail() || file_2.fail() )
  {
    std::cout << "error: can't open files, now exiting ..." << std::endl;
    exit (EXIT_FAILURE);
  }

  std::string dummy_1;
  std::string dummy_2;

  while(!file_1.eof()) // dummy condition
  {
    std::getline(file_1,dummy_1);
    std::getline(file_2,dummy_2);
    std::cout << ((dummy_1==dummy_2) ? "= " : "# ") << dummy_1 << std::endl << "  " << dummy_2 << std::endl;
  }

  return(0);
}

これが私のガイドラインです:

  • 2つのファイルを比較する
  • ユーザーは、この2つのファイルの名前を実行可能ファイルに直接渡す必要があります。この2つの引数のみです。
  • C++で可能な限り多くのエラー処理をカバーするため
  • プラットフォーム固有の手順や移植性のないコードは避けてください

私の実際の問題は、ダミーの状態を効果的に改善する方法がわからないことです。今のところ、whileの反復は最初に渡されたファイルの長さに従うだけです。両方のファイルを明らかに下に移動し、前にこの2つのファイルの長さを取得して比較するために、余分なシクルのようなやり過ぎを導入せずにこれを解決したいと思います。実際の比較を行います。

また、私のアプローチが安全であると考えられるかどうかも知りたいです。

最終的には、Boostライブラリは非常に移植性が高く、他の理由で使用することをすでに知っているため、Boostライブラリを使用したソリューションを提案する回答を受け入れることもできます。

ありがとう。

4

3 に答える 3

3

ジョンが指摘したように。通常、条件でeof()を使用するのは誤りです。

しかし、この場合は適切だと思います。ただし、結果として、いくつかのチェックを追加する必要があります。

while(true)  // exit provided by break.
{
    std::string dummy_1;   // By declaring them here you force them to be 
    std::string dummy_2;   // reset each iteration.

    // Because you are doing the read inside the loop
    // You need to check if the reads work.
    if (!std::getline(file_1,dummy_1) && !std::getline(file_2,dummy_2))
    {
        // Only exit if both reads fail.
        break;
    }

    // Got here if at least one read worked.
    // A failed read will result in an empty line for comparison.    
    std::cout << ((dummy_1==dummy_2) ? "= " : "# ") << dummy_1 << std::endl << "  " << dummy_2 << std::endl;
}
于 2012-11-11T15:15:42.453 に答える
3

いつもeof()のように行うのは間違ったことです。これは動作します

while (std::getline(file_1, dummy_1) && std::getline(file_2, dummy_2))
{
    ...
}

実際に何をしているのかを読んでみることをお勧めeof()します。思ったとおりではありませんが、実際には、2つのファイルのどちらがファイルの終わりに到達したかを判断するために適切な方法で使用できるため、このプログラムで役立ちます。こちらをご覧ください

このプログラムで実際に適切に使用eof()して、2つのファイルのどちらがファイルの終わりに到達したかを見つけることができます。私はおそらくあなたのループをこのように書くでしょう

for (;;)
{
    getline(file_1, dummy_1);
    getline(file_2, dummy_2);
    if (file_1.eof() || file_2.eof())
        break;
    ...
}
if (file_1.eof() && file_2.eof())
{
    // both at end of file
}
else if (file_1.eof())
{
    // file 1 at end of file
}
else
{
    // file 2 at end of file
}

eof()ただし、テストは前ではなく、後に行われることに注意してくださいgetline()。それeof()が使用されることになっている方法です。

于 2012-11-11T15:00:29.857 に答える
3

私は@LokiAstariの答えにかなり長いコメントを書くことから始めましたが、それは十分に長いので(そして、IMO、仕事をするのに十分クリーンな方法)、おそらく独立した答えとして最も理にかなっています。この場合、ファイルの1つからの読み取りが成功する限り読み取りを続けることを除いて、標準ループに近いものが必要です。その場合、@ johnは正しいのでeof()、ループ条件の一部として使用することは避けるのが最善です。

std::string line1, line2;
static const char *prefixes[] = {"#  ", "=  "};


while(std :: getline(file_1、line1)|| std :: getline(file_2、line2))std :: cout << prefixes [line1 == line2] << line1 << "\ n" << line2 << "\ n";

編集:@ user1802174は良い点を提起しました-それがそうであったように、ループは実際にはデータをまったく並列に読み取っていませんでした。||短絡評価を行うを使用していたため、最初のファイルからの読み取りが成功した場合、2番目のファイルからは何も読み取れませんでした。幸いなことに、彼は1つのことについて間違っていました。それは、修正するのがかなり簡単なことです。少なくともこの場合、+結果をに明示的にキャストする必要がありますが、正常に機能しboolます。getlineまた、失敗したときに文字列の以前の内容がそのまま残るという事実の修正を追加しました。そのため、目的の動作を得るには、ループの反復ごとに文字列を明示的にクリアする必要があります。

while (line1.clear(), line2.clear(), 
      (bool)std::getline(file_1, line1) + (bool)std::getline(file_2, line2))
{
    std::cout << prefixes[line1==line2] << line1 << "\n   " << line2 << "\n";
}

今回は簡単なテストを行いました。ファイル1:

line1
line 2

ファイル2:

line 1
line 2
line 3

結果:

#  line1
   line 1
=  line 2
   line 2
#
   line 3

明らかにまだ本格的なdiffユーティリティではありませんが、これは意図したとおりに機能していると思います。

@Loki Astariの回答のように、これは基本的に、行の少ないファイルの最後に、長いファイルと一致するように必要な数の空の行が埋め込まれたかのように機能します。

余談ですが、の"\n"代わりにの使用にも注意してくださいstd::endl。改行を挿入するstd::endlだけでなく、出力バッファもフラッシュします。この場合、これはほぼ確実に不要です。バッファをフラッシュしても正しい結果が得られますが、多くの場合、処理がはるかに遅くなる可能性があります。

編集:コーディングスタイルに関しては、ループを:ではなくループとして記述する方おそらく少し良いでしょう。forwhile

for ( ; (bool)std::getline(file_1, line1) + (bool)std::getline(file_2, line2))
      ; line1.clear(), line2.clear())
{
    std::cout << prefixes[line1==line2] << line1 << "\n   " << line2 << "\n";
}

私は個人的に、ここでC++スタイルのキャストを使用することによる実際の利益はほとんど見られません。の使用をやめたい場合は(bool)、おそらく別のよく知られたイディオムを使用します(確かに、多くの人も嫌いです)。

for ( ; !!std::getline(file_1, line1) + !!std::getline(file_2, line2))
      ; line1.clear(), line2.clear())
{
    std::cout << prefixes[line1==line2] << line1 << "\n   " << line2 << "\n";
}

誰かが本当にコンマ演算子の使用に反対している場合、これは次のように簡単に書き直すことができます。

while (!!std::getline(file_1, line1) + !!std::getline(file_2, line2))       
{
    std::cout << prefixes[line1==line2] << line1 << "\n   " << line2 << "\n";
    line1.clear();
    line2.clear();
}

個人的には改善とは思いませんが、反対する人もいるかもしれません。

于 2012-11-11T16:02:50.060 に答える