2017-10-25 7 views
-2

ファイルの内容を特定のオブジェクトに読み込む必要があります。残念ながらstd :: stringを使用する自由がないため、charポインタを使用する必要があります。 しかし、私はこれを行うときに私は奇妙な兆候をメモリから直接来て、solutionしかし、それは動作していません。 したがって、stdライブラリの代わりにistreamから直接getlineを使って再構築しましたが、同じことが起こります。 std :: stringを使用せずにファイルを正しく読み取るにはどうすればよいですか。C++でcharポインタのみを持つファイルを読む

PortsContainer game::ParsePort(std::istream& stream) 
{ 
    PortsContainer ports; 

    bool passFirstRow = false; 

    char* portLine = new char[1000000]; 
    int i = 0; 
    while (!stream.eof()) 
    { 
     if (!stream) 
      throw std::system_error(Error::STREAM_ERROR); 

     if (portLine[0] == '\0' || portLine == nullptr || portLine[0] == '#') 
      continue; 

     std::stringstream ss(portLine); 

     if (!passFirstRow) { 
      char* name = new char[100]; 
      while (!ss.eof()) { 
       ss.getline(name, sizeof name, ';'); 
       Port* port = new Port(); 
       //port->name = const_cast<char*>(name); 
       port->name = const_cast<char*>(name); 
       ports.addItem(port); 
      } 

      passFirstRow = true; 
     } else { 
      i++; 
     } 

     if (!stream) 
      throw std::system_error(Error::STREAM_ERROR); 
    } 

    return ports; 
} 

PortsContainer game::ParsePort(std::istream& stream, std::error_code& errorBuffer) 
{ 
    try 
    { 
     return ParsePort(stream); 
    } 
    catch (std::system_error exception) 
    { 
     errorBuffer = exception.code(); 
    } 
} 

PortsContainer game::GetAvailablePorts() 
{ 
    PortsContainer ports; 

    std::ifstream stream("./ports.csv"); 

    std::error_code errorBuffer; 
    ports = ParsePort(stream, errorBuffer); 
    if (errorBuffer) 
     return PortsContainer(); 

    return ports; 
} 
+2

最初に私はあなたが[なぜiostream :: eof insideループ状態が間違っていると思われますか?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong)次に、C++の 'char'文字列は本当に***ヌル終了文字列**と呼ばれていることを覚えておいてください。ヌルターミネータ(ヌルポインタと混同しないでください)は重要です。また、あなたが割り当てたメモリは初期化されず、読み込みさえ*未定義の動作につながることを覚えておいてください。最後に、あなたはたぶんここにいくつかのメモリリークを持っているでしょう。 –

+1

'sizeof name'どのような価値がありますか? –

+3

std :: string_ YAIT(さらに別の無能な先生)を使用する自由がありません –

答えて

1

portLineにはデータが入力されていません。実際には、streamからのデータはまったく読み込まれていません。

あなたは誤って使用していますeof()eofbitフラグは、読み取り操作が最初に試行されるまで更新されません。だから、eof()を溶かす前に必ず読む必要があります。

portLinenameのバッファが漏れています。さらに悪いことに、std::stringを使用することが許可されていないため、Port::nameメンバーがchar*ポインターであることを意味します。つまり、複数のPortオブジェクトがメモリ内の同じ物理バッファを指しています。 Portがデストラクタなどでそのバッファを解放しようとすると、メモリエラーが発生します。

代わりに、より多くのこのような何かを試してみてください:

PortsContainer game::ParsePort(std::istream& stream) 
{ 
    if (!stream) 
     throw std::system_error(Error::STREAM_ERROR); 

    PortsContainer ports; 

    bool passFirstRow = false; 

    // better would be to use std::unique_ptr<char[]>, std::vector<char>, 
    // or std::string instead so the memory is freed automatically ... 
    char *portLine = new char[1000000]; 

    int i = 0; 

    do 
    { 
     if (!stream.getline(portLine, 1000000)) 
     { 
      delete[] portLine; // <-- free the buffer for line data... 
      throw std::system_error(Error::STREAM_ERROR); 
     } 

     if ((stream.gcount() == 0) || (portLine[0] == '#')) 
      continue; 

     if (!passFirstRow) 
     { 
      std::istringstream iss(portLine); 

      // better would be to use std::unique_ptr<char[]>, std::vector<char>, 
      // or std::string instead so the memory is freed automatically ... 
      char* name = new char[100]; 

      while (iss.getline(name, 100, ';')) 
      { 
       if (iss.gcount() == 0) continue; 

       Port *port = new Port(); 
       port->name = name; // <-- assumes ownership is transferred! 
       ports.addItem(port); 
       name = new char[100]; // <-- have to reallocate a new buffer each time! 
      } 
      delete[] name; // <-- free the last buffer not used... 
      passFirstRow = true; 
     } else { 
      ++i; 
     } 
    } 
    while (!stream.eof()); 

    delete[] portLine; // <-- free the buffer for line data... 

    return ports; 
} 

PortsContainer game::ParsePort(std::istream& stream, std::error_code& errorBuffer) 
{ 
    try 
    { 
     return ParsePort(stream); 
    } 
    catch (const std::system_error &exception) 
    { 
     errorBuffer = exception.code(); 
     return PortsContainer(); // <-- don't forget to return something! 
    } 
} 

PortsContainer game::GetAvailablePorts() 
{ 
    std::ifstream stream("./ports.csv"); 
    std::error_code errorBuffer; 
    return ParsePort(stream, errorBuffer); // <-- no need to check errorBuffer before returning! 
} 

をしかし、私は強くあなたが安全なメモリ管理を確保するために、STLスマートポインタを使用することをお勧め:

PortsContainer game::ParsePort(std::istream& stream) 
{ 
    if (!stream) 
     throw std::system_error(Error::STREAM_ERROR); 

    PortsContainer ports; 

    bool passFirstRow = false; 

    // since you are using std::error_code, that means you are 
    // using C++11 or later, so use std::unique_ptr to ensure 
    // safe memory management... 
    std::unique_ptr<char[]> portLine(new char[1000000]); 

    int i = 0; 

    do 
    { 
     if (!stream.getline(portLine.get(), 1000000)) 
      throw std::system_error(Error::STREAM_ERROR); 

     if ((stream.gcount() == 0) || (portLine[0] == '#')) 
      continue; 

     if (!passFirstRow) 
     { 
      std::istringstream iss(portLine.get()); 

      // use std::unique_ptr here, too... 
      std::unique_ptr<char[]> name(new char[100]); 

      while (iss.getline(name.get(), 100, ';')) 
      { 
       if (iss.gcount() == 0) continue; 

       // use std::unique_ptr here, too... 
       std::unique_ptr<Port> port(new Port); 
       port->name = name.release(); // <-- assumes ownership is transferred! 
              // better to make Port::name use std::unique_ptr<char[]> and then std::move() ownership of name to it... 
       ports.addItem(port.get()); 
       port.release(); 

       name.reset(new char[100]); // <-- have to reallocate a new buffer each time! 
      } 
      passFirstRow = true; 
     } else { 
      ++i; 
     } 
    } 
    while (!stream.eof()); 

    return ports; 
} 

ものの、std::stringを使用するだろう最高のオプション:

PortsContainer game::ParsePort(std::istream& stream) 
{ 
    if (!stream) 
     throw std::system_error(Error::STREAM_ERROR); 

    PortsContainer ports; 

    bool passFirstRow = false; 
    std::string portLine; 
    int i = 0; 

    while (std::getline(stream, portLine)) 
    { 
     if (portLine.empty() || (portLine[0] == '#')) 
      continue; 

     if (!passFirstRow) 
     { 
      std::istringstream iss(portLine); 
      std::string name; 

      while (std::getline(iss, name, ';')) 
      { 
       if (name.empty()) continue; 

       std::unique_ptr<Port> port(new Port); 
       port->name = name; // <-- make Port::name be std::string as well! 
       ports.addItem(port.get()); 
       port.release(); 
      } 
      passFirstRow = true; 
     } else { 
      ++i; 
     } 
    } 

    if (!stream) 
     throw std::system_error(Error::STREAM_ERROR); 

    return ports; 
} 
+0

あなたのサポートと説明に感謝します。私はそれがクラッシュする最後に大部分のために今働いていますが、それは別のセクションに私は修正する必要があります。文字列に関連して、私はそれもばかげていると思うが、残念なことにそれについて何もできない。学生がポインタの背後にあるロジックを理解し、ヒープとそのようなものを理解できるようにするためにコンテナのものが得られますが、std :: stringのものは「単にコンテナではないのですか?あなたもそれを使用することはできません "。その背後に論理がないので、怒りの管理以外に何も教えていない。 –

+0

オリジナルの回答はより関連性がありました。私の前のコメントが示唆しているように、コースのこの部分は学生/私にメモリ管理を学ばせたいので、スマートなポインタは許されません。何らかの理由で私はメモリリークを取得しますが、私はそれらを解決することができます。あなたのソリューションをお寄せいただきありがとうございます。 –

+0

私は元の 'char *'ポインタを使ってコードを元に戻しましたが、別のコードを別の提案として残しました。努力のために –

関連する問題