使用 Write 成员将 CString 写入 CFile 时每个字符后的 Null

Posted

技术标签:

【中文标题】使用 Write 成员将 CString 写入 CFile 时每个字符后的 Null【英文标题】:Nulls after each character when writing CString to CFile using Write member 【发布时间】:2016-11-17 23:54:02 【问题描述】:

我目前正在研究 MFC 库 CFile 类,但在使用 Write 方法将数据写入文件时遇到问题。当我将 char 数组作为参数传递时,它工作得很好:

char c[] = "Hello!"; 
int size = sizeof(c) / sizeof(c[0]);
myFile.Write(c, size)

写入文件的字符:

Hello!

但是当我尝试将 CString 对象作为参数传递时:

CString cS("Hello");
myFile.Write(cS, cS.GetLength());

我明白了:

H e l

我也试过了:

CString cS("Hello");
LPWSTR c = cS.GetBuffer();
myFile.Write(c, cS.GetLength());
cS.ReleaseBuffer();

但是输出和上面一样。什么可能导致这种转换?是否因为文本存储在宽字符中而发生?

【问题讨论】:

使用CStringA 而不是CString。 ASCII 范围内的宽字符由一个字节 ASCII 和一个空字节组成。 【参考方案1】:

问题:

CFile::Write 的第二个参数是函数将从第一个参数(缓冲区)传输的字节数。您正在传递cS.GetLength(),它宁愿传递字符串中的字符数,这与字符串本身可能包含的字节数不同。

解决方案:

您应该将将字符串写入文件的行更改为:

myFile.Write(LPCTSTR(cS), cS.GetLength()*sizeof(TCHAR));

sizeof(TCHAR) 将根据您是为 Unicode 还是 MBCS 构建不同的数字。这是因为TCHAR 被定义为wchar_t 用于Unicode 构建,char 用于MBCS 构建。因此,将字符串的长度乘以 TCHAR 的大小将始终等于字符串所包含的字节数,无论您是否为 Unicode 构建。

其他注意事项:

你没有理由在这里打电话给GetBuffer()ReleaseBuffer(),无论如何。

这点不是主要的,但CFile::Write 函数将const void * 作为它的第一个参数。因此,您应该将CString 转换为LPCTSTR(根据您是使用Unicode 还是MBCS 构建,它会自动评估为LPCWSTRLPCSTR)。

最后一件事:最好用 _T() 宏包装字符串文字,这样您就可以针对 Unicode 和 MBCS 进行编译,而无需更改代码。

应用所有更改,您的整个代码将如下所示:

CString cS(_T("Hello"));
myFile.Write(LPCTSTR(cS), cS.GetLength()*sizeof(TCHAR));

【讨论】:

语义上,sizeof(TCHAR) 应该是sizeof(cS::XCHAR)。这是所有CStringT 派生字符串类型的预定义字符类型。我还建议不要显式转换。隐式调用转换运算符。如果你想明确一点,你可以把它称为GetString() 成员。这样做可以防止无意中调用转换运算符。 @IInspectable 我不认为你可以输入sizeof(cS::XCHAR) 并让它编译。我想你可能指的是sizeof(CString::XCHAR),但这与sizeof(TCHAR) 完全相同。另外,请注意LPCTSTR 实际上是一个运算符:msdn.microsoft.com/en-us/library/aa300569(v=vs.60).aspx “我想你的意思可能是sizeof(CString::XCHAR),但这与sizeof(TCHAR) 完全一样。” - 没错,它们指定了相同的类型。但是,存在语义上的区别:TCHAR 只是一个不相关的类型,XCHAR 是整个CString 实现中使用的类型。 “另外,请注意LPCTSTR 实际上是一个运算符” - 这就是我之前将其称为“转换运算符” 的原因。它仍然被隐式调用。文档的正确链接是here。 “我不认为你可以输入 sizeof(cS::XCHAR) 并让它编译。” - 正确,我的意思是写 sizeof( decltype( cS )::XCHAR )。对于任何给定的CString 实例,无论项目设置如何,总是 返回字符代码单元的正确大小。这很重要,因为您可以在 Unicode 项目中使用 CStringA,或在 MBCS 项目中使用 CStringW,因此 sizeof( TCHAR ) 会产生错误的值。 @IInspectable 太糟糕了CString 没有定义一个等于sizeof(XCHAR) 的常量成员,它会让这变得容易得多。【参考方案2】:

那是因为你在编译时定义了UNICODE,而CString 是一个宽字符字符串,每个字符占用两个字节。写入文件的内容是您所看到的字符字节,后跟零字节。

【讨论】:

值得编辑以明确声明 GetLength 将返回字符数,而不是字节数,因此是 Write 要求的一半。【参考方案3】:

我已经在 MFC、Visual C++ 6.0 到 Visual C++ 2005 中进行了大约 4 年的专业开发,以支持我们公司的应用程序。

根据我的经验和我对CString 类对象的了解,它们总是NULL 终止。这可能会导致与使用字符数组不同的行为,但不是上述问题。

我相信您的问题与您作为参数传入的缓冲区有关。 LPWSTR 是一个 32 位指针,指向每个 MSDN Reference 的 16 位字符的字符串。

根据您发布的内容,据我所知,您正在输出 16 位 Unicode 字符并将它们视为 ANSI因此这种行为是可以预料的。如果您以Unicode 的身份打开记事本文件,您将看不到空格。

或者,如果您使用 ANSI 字符集构建项目,或在写入文件之前转换为 ANSI,则在记事本中打开输出时空格应该会消失。

【讨论】:

【参考方案4】:

试试这个

TCHAR c[] = "Hello!"; 
int charCount = sizeof(c) / sizeof(c[0]);
myFile.Write(c, charCount*sizeof(c[0]));

Write 将只写入指定的字节数。 charCount 将与 ASCII 的字节数相同,但将是 UNICODE 值的一半。

尝试根据您的文本类型更改上述代码

https://msdn.microsoft.com/en-us/library/6337eske.aspx

【讨论】:

以上是关于使用 Write 成员将 CString 写入 CFile 时每个字符后的 Null的主要内容,如果未能解决你的问题,请参考以下文章

JAVA write为啥无法写入数据?

使用 write() 将两个字符串连接到 C 中的标准输出

R语言write函数:使用write函数将数据写入RStudio控制台write函数将数据写入新的Rstudio窗口(启动新的窗口)

如何在使用 utl_file write plsql 将数据写入 .csv 文件时处理中文/日文字符

MFC 将含中文的CString写入文本

Linux C 文件与目录3 文件读写