>

다음 형식의 이진 레코드 파일을 만들고 있습니다 :

quantity-of-records  
record_1  
record_2  
...  
record_N 

문제는 그 record_1 입니다  추가하는 대신 매번 덮어 씁니다.

BOF에 글을 쓴 후 EOF에 글쓰기

간단한 코드는 다음과 같습니다.

#include <fstream>
#include <string>
struct Record
{
    unsigned int    id;
    std::string     text;
};

int main()
{
    static const Record     table[] =
    {
        {
            1, "Apple"
        },
        {
            2, "Salt"
        },
        {
            3, "Margarine"
        },
        {
            4, "Carrot"
        },
        {
            5, "Plum"
        }
    };
    static const size_t records_in_table =
        sizeof(table) / sizeof(table[0]);
    static const char   table_filename[] = "record_file.bin";
    size_t i;
    size_t record_quantity = 1u;
    for (i = 0u; i < records_in_table; ++i)
    {
        std::ofstream   table_file(table_filename,
                                   std::ios::binary);
        table_file.seekp(0, std::ios::beg);
        table_file.write(reinterpret_cast<char *>(&record_quantity),
                         sizeof(record_quantity));
        table_file.flush();
        table_file.seekp(0, std::ios::end);
        table_file.write(reinterpret_cast<char const *>(&table[i].id),
                         sizeof(Record::id));
        const size_t length(table[i].text.length());
        table_file.write(reinterpret_cast<char const *>(&length),
                         sizeof(length));
        table_file.write(table[i].text.c_str(),
                         length);
        table_file.close();
        ++record_quantity;
    }
    return 0;
}

이진 파일의 내용은 다음과 같습니다.

$ od -Ax -x record_file.bin
000000 0005 0000 0000 0000 0005 0000 0004 0000
000010 0000 0000 6c50 6d75
000018

숫자는 Little Endian 형식으로32 비트 (4 바이트)64 비트 (8 바이트)로 작성됩니다. 텍스트 "Plum"은 0x50, 0x6C, 0x75, 0x6D와 같이 ASCII로 인코딩됩니다.

첫 번째 반복 후 바이너리 파일은 다음과 같습니다.

$ od -Ax -x record_file.bin
000000 0001 0000 0000 0000 0001 0000 0005 0000
000010 0000 0000 7041 6c70 0065
000019

환경/도구 :

  • 컴파일러 : Visual Studio 2017, G ++ (GCC) 7.4.0 (Cygwin)
  • OS : Windows 7
app 모드로 열기

대안은 ios::app 에서 파일을 여는 것입니다  모드에서 새 레코드를 작성한 다음 레코드 수를 업데이트합니다.

size_t  i;
size_t  record_quantity = 1u;
bool    first_write(true);
for (i = 0u; i < records_in_table; ++i)
{
    std::ofstream   table_file(table_filename,
                               std::ios::binary | std::ios::app);
    if (first_write)
    {
        first_write = false;
        table_file.write(reinterpret_cast<char *>(&record_quantity),
                         sizeof(record_quantity));
        table_file.flush();
        table_file.write(reinterpret_cast<char const *>(&table[i].id),
                         sizeof(Record::id));
        const size_t length(table[i].text.length());
        table_file.write(reinterpret_cast<char const *>(&length),
                         sizeof(length));
        table_file.write(table[i].text.c_str(),
                         length);
    }
    else
    {
        table_file.write(reinterpret_cast<char const *>(&table[i].id),
                         sizeof(Record::id));
        const size_t length(table[i].text.length());
        table_file.write(reinterpret_cast<char const *>(&length),
                         sizeof(length));
        table_file.write(table[i].text.c_str(),
                         length);
        table_file.flush();
        table_file.seekp(0, std::ios::beg);
        table_file.write(reinterpret_cast<char *>(&record_quantity),
                         sizeof(record_quantity));
    }
    table_file.close();
    ++record_quantity;
}

그러나 대체 구현에서는 파일의 레코드 수 또는 파일의 첫 번째 정수가 업데이트되지 않습니다.
이진 파일의 내용은 다음과 같습니다.

$ od -Ax -x record_file.bin
000000 0001 0000 0000 0000 0001 0000 0005 0000
000010 0000 0000 7041 6c70 0165 0000 0000 0000
000020 0100 0000 0500 0000 0000 0000 4100 7070
000030 656c 0002 0000 0004 0000 0000 0000 6153
000040 746c 0002 0000 0000 0000 0003 0000 0009
000050 0000 0000 0000 614d 6772 7261 6e69 0365
000060 0000 0000 0000 0400 0000 0600 0000 0000
000070 0000 4300 7261 6f72 0474 0000 0000 0000
000080 0500 0000 0400 0000 0000 0000 5000 756c
000090 056d 0000 0000 0000 0000
000099

질문 : 파일 끝에 레코드를 추가하고 첫 번째 정수 (파일 시작 부분)를 업데이트하려면 어떻게해야합니까?

  • 답변 # 1

    루트 원인

    근본 원인 또는 문제는 파일을 여는 모드입니다. 내 실험에 따르면 std::ios_base::app 로 파일을 열 때만 데이터가 추가됩니다. . 그러나 대부분의 문서는모든쓰기가 파일에 추가 될 것을 암시합니다. 위치를 찾은 후에도 EOF에 데이터가 기록됩니다.

    파일의 시작 부분을 자르지 않고 쓰려면 ofstream   std::ios_base::in 로 열어야합니다  그리고 std::ios_base::out  속성.

    수정 된 프로그램

    레코드가 16 바이트 경계에 정렬되고 사용되지 않은 바이트가 0xFF로 채워지도록 프로그램을 수정했습니다 (이는 16 진 덤프를보다 읽기 쉽게 만듭니다). 모든 정수 데이터는 32 비트입니다. 텍스트는 가변 길이입니다.

    파일에 추가하기 위해 레코드 데이터가 먼저 기록됩니다. 각 모드에서 한 번에 두 개의 다른 변수를 사용하여 파일이 두 번 열립니다.

    #include <fstream>
    #include <string>
    struct Table_Quantity_Record
    {
        unsigned int    quantity;
        uint8_t         padding[12];
    };
    struct Record
    {
        unsigned int    id;
        std::string     text;
    };
    
    int main()
    {
        static const Record     table[] =
        {
            { 0x11111111, "Apple"},
            { 0x22222222, "Salt"},
            { 0x33333333, "Butter"},
            { 0x44444444, "Carrot"},
            { 0x55555555, "Plum"},
        };
        static const size_t records_in_table =
            sizeof(table) / sizeof(table[0]);
        static const char   table_filename[] = "record_file.bin";
        std::remove(&table_filename[0]);
        size_t  i;
        Table_Quantity_Record   quantity_record;
        quantity_record.quantity = 1;
        std::fill(&quantity_record.padding[0],
                  &quantity_record.padding[12],
                  0xffu);
        static const uint8_t    padding_bytes[16] = {0xFFu};
        for (i = 0; i < records_in_table; ++i)
        {
            // Open the file in append mode, and append the new data record.
            std::ofstream   data_file(&table_filename[0],
                                      std::ios_base::binary | std::ios_base::app | std::ios_base::ate);
            if (data_file)
            {
                data_file.write((char *) &table[i].id, sizeof(Record::id));
                const unsigned int length = table[i].text.length();
                data_file.write((char *) &length, sizeof(length));
                data_file.write(table[i].text.c_str(), length);
                data_file.flush();
                const unsigned int padding_qty =
                    16 - sizeof(Record::id) - sizeof(length) - length;
                static const uint8_t pad_byte = 0xFFU;
                for (size_t j = 0; j < padding_qty; ++j)
                {
                    data_file.write((char *) &pad_byte, sizeof(pad_byte));
                }
                data_file.flush();
                data_file.close();
            }
            // Open the data file with "in" attribute to write the record quantity
            // at the beginning of the file.
            std::ofstream   table_file(&table_filename[0],
                                       std::ios_base::binary | std::ios_base::in);
            table_file.write((char *) &quantity_record, sizeof(quantity_record));
            table_file.flush();
            table_file.close();
            ++quantity_record.quantity;
        }
        return 0;
    }
    
    

    이진 파일의 내용

    $ od -Ax -x record_file.bin
    000000 0005 0000 ffff ffff ffff ffff ffff ffff
    000010 2222 2222 0004 0000 6153 746c ffff ffff
    000020 3333 3333 0006 0000 7542 7474 7265 ffff
    000030 4444 4444 0006 0000 6143 7272 746f ffff
    000040 5555 5555 0004 0000 6c50 6d75 ffff ffff
    000050
    
    

    참고 : 문제의 프로그램 이후 레코드 ID 값이 변경되어 레코드를 쉽게 찾을 수 있습니다.

  • 답변 # 2

    파일 스트림을 여는 방법 때문에 문제가 발생합니다 와이즈 비즈 . 출력을 위해서만 열기 때문에 기존 파일 내용은 table_file 의 유무에 관계없이 파괴됩니다  열린 모드.

    기존 컨텐츠에 추가하려면 ios:trunc 를 포함해야합니다.  전화의 일부로 :

    ios:::read
    
    

    ( std::ofstream table_file(table_filename, std::ios::binary | std::ios::in);  필요한 ofstream 를 추가합니다  파일에 쓸 때 읽기 모드를 요구하는 것은 직관적이지 않은 것처럼 보일 수 있지만 파일 중간에 쓸 필요가있을 때마다 기존 콘텐츠 중 일부를 읽어야합니다. 쓰기가 장치 저장 영역 경계 (섹터 또는 클러스터)에 잘 맞지 않을 수 있습니다.

    std::ios::write

  • 이전 sql - 기본 키 열과 데이터를 가져 오지 않는 두 개의 외래 키 열이있는 조인 테이블을 만들었습니다
  • 다음 tuples - Haskell에서 '비 완전 패턴 기능'오류를 수정하는 방법?