在二进制文件c ++中读取和写入字符串

时间:2014-05-23 08:49:00

标签: c++ visual-studio-2010 unicode-string wstring

我正在尝试开发一个小的 Windows 应用程序,以提高我在MFC框架之外的C ++技能,并帮助我学习外语。
我想做一个小的,个人的和easy-to-port_and_use字典一样,虽然我在开发GUI方面没有任何问题,但我在保存和恢复数据方面真的很痛苦。

我的想法是写下一个二进制文件结构为遵循:

int (representing the number of words)
int (representing the string length + \0)
sequence of characters zero-terminated.
现在,我正在学习俄语,我的主要语言是意大利语,所以我不能使用普通的旧std :: string来记下单词,而且,谢谢微软,我正在使用VS2010包含所有商品和{{3随之而来的。我正在向你展示我的惯例,写下int和wstring:
//Writing int
void CDizionario::ScriviInt( int nInt, wofstream& file ) const
{
    file.write( reinterpret_cast < const wchar_t * > ( &nInt ), sizeof( nInt ) );
    file.flush();
}
// Writing string
void CDizionario::ScriviWString( int nLStringa, const wstring* pStrStringa, wofstream& file ) const
{
    wchar_t cTerminatore;
    string strStringa;
    file.write( pStrStringa->c_str(), nLStringa );
    file.flush();
    cTerminatore = L'\0';
    file.write( &cTerminatore, sizeof( wchar_t ) );
    file.flush();
}
// Reading int
void CDizionario::LeggiInt( int *pInt, wifstream& file )
{
    file.read( reinterpret_cast < wchar_t * >( pInt ), sizeof( int ) );
}
// Reading wstring
void CDizionario::LeggiWString( int nLStringa, wstring& strStringa, wifstream& file )
{
    wchar_t *pBuf;
    streamsize byteDaLeggere;
    byteDaLeggere = nLStringa;
    pBuf = new wchar_t[(unsigned int)( byteDaLeggere * sizeof( wchar_t ) )];
    file.read( pBuf, byteDaLeggere * sizeof( wchar_t ) );
    strStringa.append( pBuf );
    delete [] pBuf;
}
// Constructor
CDizionario::CDizionario( void )
{
    m_pLoc = new locale( locale::classic(), new codecvt_utf8_utf16 );
}
// Somewhere in my code before calling LeggiInt/ScriviInt/LeggiWString/ScriviWString:
// ...
file.imbue( *m_pLoc );

好吧,我的第一次测试是: ciao - привет,结果:

01 00 ee bc 90 22 05 00 ee bc 90 22 63 69 61 6f
00 ec b3 8c 07 00 ee bc 90 22 d0 bf d1 80 d0 b8
d0 b2 d0 b5 d1 82 00 ec b3 8c
数字被正确读取,问题出现在我写下字符串时:我希望 ciao (63 69 61 6f 00 ec b3 8c)写成10个字节(wchar_t大小)而不是5个字节,就像俄语翻译一样(d0 bf d1 80 d0 b8 d0 b2 d0 b5 d1 82 00 ec b3 8c)。
显然我错过了什么,但我无法弄清楚它是什么。你能帮助我吗?此外,如果你知道一个更好的方法来解决问题,我就是开放的。

编辑:解决方案
按照@JamesKanze提出的两种方法中的第一种方法,我决定牺牲一些可移植性并让系统完成我的作业:

void CDizionario::LeggiInt( int *pInt, ifstream& file )
{
    file.read( reinterpret_cast( pInt ), sizeof( int ) );
}

void CDizionario::LeggiWString( int nLStringa, wstring& strStringa, ifstream& file ) { char *pBuf; streamsize byteDaLeggere; wstring_convert> converter; byteDaLeggere = nLStringa; pBuf = new char[byteDaLeggere]; file.read( pBuf, byteDaLeggere ); strStringa = converter.from_bytes( pBuf ); delete [] pBuf; }

void CDizionario::ScriviInt( int nInt, ofstream& file ) const { file.write( reinterpret_cast( &nInt ), sizeof( nInt ) ); file.flush(); } void CDizionario::ScriviWString( const wstring* pStrStringa, ofstream& file ) const { char cTerminatore; string strStringa; wstring_convert> converter; strStringa = converter.to_bytes( pStrStringa->c_str() ); ScriviInt( strStringa.length() + 1, file ); file.write( strStringa.c_str(), strStringa.length() ); file.flush(); cTerminatore = '\0'; file.write( &cTerminatore, sizeof( char ) ); file.flush(); }

2 个答案:

答案 0 :(得分:1)

您没有充分指定二进制文件的格式。 你如何表示int(多少字节,big-endian或 little-endian),也不是编码和格式 字符。经典的网络表示将是 一个big-endian四字节(无符号)整数和UTF-8。以来 这是你为自己做的事情,你可以(和 可能应该简化,使用little-endian表示整数,和 UTF-16LE;这些格式对应于内部格式 视窗的。 (请注意,此类代码不可移植,甚至不可移植 在同一架构上的Apple或Linux,以及它 数据在新系统上变得不可读的可能性很小。) 这基本上就是你想要的,但是......

您正在尝试编写原始二进制文件。唯一标准的方法 这将是使用std::ofstream(和std::ifstream来实现的 读取),文件以二进制模式打开 "C"语言环境。对于其他任何事情,都会(或可能)有一些 std::filebuf中的代码转换和映射。 鉴于此(以及这种写入数据的方式不是这样的事实 便携式到任何其他系统),你可能只想使用 系统级功能:CreateFile打开,WriteFileReadFile写和读,CloseHandle关闭。 (看到 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364232%28v=vs.85%29.aspx)。

另一方面,如果你想要便携,我会推荐 使用标准网络格式的数据。将其格式化为 一个缓冲区(std::vector<char>),然后写出来;在另一个 结束,读入缓冲区并解析。读写 整数的例程(实际上是无符号整数)可能是 类似的东西:

void
writeUnsignedInt( std::vector<char>& buffer, unsigned int i )
{
    buffer.push_back( (i >> 24) & oxFF );
    buffer.push_back( (i >> 16) & oxFF );
    buffer.push_back( (i >>  8) & oxFF );
    buffer.push_back( (i      ) & oxFF );
}

unsigned int
readUnsignedInt( 
    std::vector<char>::const_iterator& current,
    std::vector<char>::const_iterator end )
{
    unsigned int retval = 0;
    int shift = 32;
    while ( shift != 0 && current != end ) {
        shift -= 8;
        retval |= static_cast<unsigned char>( *current ) << shift;
        ++ current;
    }
    if ( shift != 0 ) {
        throw std::runtime_error( "Unexpected end of file" );
    }
    return retval;
}

对于角色,你必须将你的std :: wstring转换为 UTF-8中的std :: string,使用许多转换例程之一 可在网络上获得。 (问题是编码 std::wstring,甚至wchar_t的大小也不是。char 标准化。在我熟悉的系统中,Windows和AIX 使用UTF-16,大多数其他UTF-32;在这两种情况下都有字节 订单依赖于平台。这使得便携式代码变得有点 更难。)

在全球范围内,我发现直接完成所有操作更容易 UTF-8,使用'\0'。这不适用于Windows 然而,界面。

最后,如果输出,则不需要尾随{{1}} 长度。

答案 1 :(得分:0)

@IssamTP,привет

正如@James Kanze所提到的,使用外来的非拉丁语言不可避免地会促使你使用每字节格式约定和语言环境。因此,重新发明轮子并使用现有技术(如XML)可能是值得的(因此该技术将提供细微差别并正确编码/解码非拉丁字符。)