2010-11-29 5 views
1

나는 Stack Overflow와 파일에 벡터를 작성하는 다른 많은 사이트에 관한 글을 읽었다. 나는 일하고 있다고 느끼는 것을 구현했지만 몇 가지 문제가 있습니다. 구조체의 데이터 멤버 중 하나는 클래스 문자열이며 벡터를 다시 읽을 때 해당 데이터가 손실됩니다. 또한 첫 번째 반복을 작성한 후 추가 반복을 수행하면 malloc 오류가 발생합니다. 원하는 코드를 파일에 저장 한 다음 프로그램을 다시 시작할 때 다시 읽으려면 아래 코드를 어떻게 수정하면됩니까? 현재, 데이터 멤버 만 벡터 인 클래스의 소멸자에 쓰는 생성자에서 읽기가 수행되지만 해당 벡터를 조작하는 메서드가 있습니다.구조체의 벡터를 읽고 파일에 쓰는 것

여기 내 읽기/쓰기 방법의 요지가 있습니다.

ifstream infile; 
infile.open("data.dat", ios::in | ios::binary); 
infile.seekg (0, ios::end); 
elements.resize(infile.tellg()/sizeof(element)); 
infile.seekg (0, ios::beg); 
infile.read((char *) &elements[0], elements.capacity()*sizeof(element)); 
infile.close(); 

쓰기 :

ofstream outfile; 
outfile.open("data.dat", ios::out | ios::binary | ios_base::trunc); 
elements.resize(elements.size()); 
outfile.write((char *) &elements[0], elements.size() * sizeof(element)); 
outfile.close(); 

구조체 요소 :

C에서
struct element { 
int id; 
string test; 
int other;   
}; 
+0

당신이 사용하는 C++ 학습 자료에서 이런 식으로 파일 액세스를 했습니까? –

+0

이것은 fstream을 사용하여 벡터 데이터를 파일로 읽고 쓰는 것을 요구하는 과제물의 일부입니다. 클래스의 파일에 복잡한 벡터 쓰기 작업을 다루지 않았기 때문에 fstream 문서 등을 읽고 온라인에서 예제를 살펴 보았습니다. 바이너리로 쓰는 것이 가장 쉬운 방법이라고 생각했습니다. 크기 조정과 같은 특정 사항은 파일 크기를 제한하기 위해 수행 한 작업이었습니다. 우리에게는 수업을위한 공식 서적이나 자료가 없습니다. 우리는 온라인 노트를 가지고 있지만, 필요한 정보를 구현하는 데 필요한만큼 상세하지 않습니다. –

+0

문자열을 문자 배열로 변경하고 해당 변경을 지원하기 위해 코드의 다른 부분을 약간 수정하기로 결정했습니다. 문자열 대신 문자 배열로 전환하면 위의 코드가 잘 작동하는 것 같습니다. –

답변

6

++, 메모리는 일반적으로 직접 기록 읽을 수없는

가 읽기 ... vector<element> elements 가정 디스크를 직접적으로 좋아하십시오. 특히 struct element에는 string 데이터 형식이 포함되어 있지 않으므로 POD 데이터 형식이 아니므로 직접 액세스 할 수 없습니다.

생각한 실험이이를 명확히하는 데 도움이 될 수 있습니다. 코드에서는 모든 element 값이 같은 크기라고 가정합니다. string test 값 중 하나가 귀하가 가정 한 값보다 길면 어떻게됩니까? 디스크에 읽고 쓸 때 사용할 크기를 코드가 어떻게 알 수 있습니까?

이 문제를 해결하는 방법에 대한 자세한 내용은 serialization을 읽어보십시오.

+0

그리고 다른 아키텍처 (endianess)의 컴퓨터에서이 파일을 읽고 쓰고 싶다면 엔디안 중립적 인 방법으로 이진 파일을 작성/읽어야합니다. – David

+0

이것은 의미가 있습니다. 나는 더 쉬운 해결책은 문자열로 전환하는 것이라고 생각한다. 클래스 문자열은 필자에게 선호되며 더 유연하게 사용할 수 있습니다. –

1

코드는 모든 관련 데이터가 벡터 안에 직접있는 것으로 가정하고 문자열은 가변 크기의 내용을 힙에 추가 할 수있는 포인터가있는 고정 크기의 개체입니다. 기본적으로 포인터가 아니라 텍스트를 저장하고 있습니다. 다음과 같이 문자열 직렬화 코드를 작성해야합니다.

bool write_string(std::ostream& os, const std::string& s) 
{ 
    size_t n = s.size(); 
    return os.write(n, sizeof n) && os.write(s.data(), n); 
} 

그런 다음 구조체에 대한 직렬화 루틴을 작성할 수 있습니다. 많은 사람들이 표준 :: ostream에를 수용 할 수 Binary_IStream/Binary_OStream 유형을 선언 좋아하지만, 구별 유형은 알라 직렬화 루틴의 별도의 세트를 만드는 데 사용할 수 있습니다되고 - :

operator<<(Binary_OStream& os, const Some_Class&); 
이 몇 가지 디자인 옵션은

또는 바이너리 직렬화를 처리 할 때 일반적인 스트리밍 표기법을 포기하고 대신 함수 호출 표기법을 사용할 수 있습니다. 분명히 동일한 코드가 이진 직렬화와 사람이 읽을 수있는 직렬화를 올바르게 출력하도록하는 것이 좋기 때문에 운영자 기반 접근법이 매력적입니다.

숫자를 일련 번호로 사용하는 경우 이진 형식으로할지 ASCII로할지 결정해야합니다. 동일한 OS에서 32 비트와 64 비트 컴파일 사이에서도 이식성이 필요한 순수한 바이너리 형식을 사용하면 형식 크기 메타 데이터 (예 : int32_t 또는 int64_t)를 인코딩하고 사용해야 할 수도 있습니다 엔디안으로 (예 : 네트워크 바이트 순서 및 ntohl() - 가족 함수를 고려). ASCII를 사용하면 이러한 고려 사항 중 일부는 피할 수 있지만 길이가 가변적이므로 쓰기/읽기 속도가 느려질 수 있습니다. 아래, 나는 임의로 ASCII를 '|' 번호 용 터미네이터.당신의 벡터

bool write_element(std::ostream& os, const element& e) 
{ 
    return (os << e.id << '|') && write_string(os, e.test) && (os << e.other << '|'); 
} 

: 그리고

os << elements.size() << '|'; 
for (std::vector<element>::const_iterator i = elements.begin(); 
    i != elements.end(); ++i) 
    write_element(os, *i); 

이 다시 읽으려면 :

std::vector<element> elements; 
size_t n; 
if (is >> n) 
    for (int i = 0; i < n; ++i) 
    { 
     element e; 
     if (!read_element(is, e)) 
      return false; // fail 
     elements.push_back(e); 
    } 

... 필요 ...

bool read_element(std::istream& is, element& e) 
{ 
    char c; 
    return (is >> e.id >> c) && c == '|' && 
      read_string(is, e.test) && 
      (is >> e.other >> c) && c == '|'; 
} 

을 ... 그리고 ...

bool read_string(std::istream& is, std::string& s) 
{ 
    size_t n; 
    char c; 
    if ((is >> n >> c) && c == '|') 
    { 
     s.resize(n); 
     return is.read(s.data(), n); 
    } 
    return false; 
}