2017-10-25 10 views
-2

파일 내용을 특정 객체로 읽어야합니다. 불행히도 std :: string을 사용할 자유가 없으므로 char 포인터를 사용해야합니다. 그러나 내가 이것을 할 때 이상한 징후가 메모리에서 직접 오는 경우 solution이 작동하지 않습니다. 그래서 표준 라이브러리 대신 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' 문자열은 실제로는 *** null-terminate ** 바이트 문자열이라고 기억해야합니다. null-terminator (널 포인터와 혼동해서는 안 됨)는 중요합니다. 또한 할당 한 메모리는 초기화되지 않고 * 읽지 않아도 * 정의되지 않은 동작으로 연결된다는 것을 기억하십시오. 마지막으로 여기 저기에 메모리 누수가있을 수 있습니다. –

+1

'sizeof name' 어떤 가치가 있습니까? –

+3

std :: string_ YAIT (아직 또 다른 무능한 교사)를 사용할 자유는 아닙니다. –

답변

1

당신은 어떤 데이터 portLine를 채우기되지 않습니다. 실제로 stream에서 데이터를 읽지 않습니다.

너는 오용하고있다. eof(). eofbit 플래그는 읽기 조작이 먼저 시도 될 때까지 갱신되지 않습니다. 그러므로 eof()을 elavulating하기 전에 반드시 읽어야합니다.

portLinename 버퍼를 유출하고 있습니다. 그리고 더 나쁜 것은 std::string을 사용할 수 없으므로 Port::name 구성원이 char* 포인터라는 의미입니다. 즉, 여러 개의 Port 개체가 메모리의 동일한 실제 버퍼를 가리킬 가능성이 있습니다. Port이 소멸자와 같이 나중에 해당 버퍼를 해제하려고하면 메모리 오류가 발생합니다. 난 강력하게 안전한 메모리 관리를 보장하기 위해 스마트 포인터를 STL을 사용하여 당신에게 제안 그러나

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! 
} 

:

대신 이와 비슷한 더 많은 것을 시도

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 *'포인터를 사용하여 코드를 되돌 렸지만 다른 코드를 추가 제안으로 사용했다. 노력을 위해 –