C语言调用WriteFile函数向txt文件写入8个中文字符串,但txt文件却显示4个
本帖最后由 mxwawaawxm 于 2022-7-24 11:19 编辑#include <windows.h>
#include <tchar.h>
void SaveFileData(PCTSTR szFileName, LPCVOID szFileData, DWORD dwNumberOfBytesToWrite);
int WINAPI WinMain(HINSTANCE hHinstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
PCTSTR szFileData = TEXT("这是一个例子。");
PCTSTR szFileName = TEXT("d:\\test\\1.txt");
SaveFileData(szFileName, szFileData, (DWORD)sizeof(szFileData));
_tprintf(TEXT("%I64u\n"), sizeof(szFileData));
return 0;
}
void SaveFileData(PCTSTR szFileName, LPCVOID szFileData, DWORD dwNumberOfBytesToWrite)
{
// 文件句柄
HANDLE hFileWrite;
// 成功写入的数据大小
DWORD dwNumberOfBytesWritten;
// 打开文件
hFileWrite = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// 判断是否打开成功
if (hFileWrite==INVALID_HANDLE_VALUE) {
_tprintf(TEXT("打开 %s 文件失败。错误代码是:%lu。\n"), szFileName, GetLastError());
} else{
// 设置文件指针到文件末
SetFilePointer(hFileWrite, 0, 0, FILE_END);
// 写入文件
if (!WriteFile(hFileWrite, szFileData, dwNumberOfBytesToWrite, &dwNumberOfBytesWritten, NULL)) {
_tprintf(TEXT("写入 %s 文件失败。错误代码是:%lu。\n"), szFileName, GetLastError());
} else {
_tprintf(TEXT("写入 %s 文件成功。写入的字节总数:%lu。\n"), szFileName, dwNumberOfBytesWritten);
}
}
CloseHandle(hFileWrite);
}
代码如上。请大佬们看看。
运行结果如下
https://pic.flymc.cc/i/hotlink-ok/2022/07/20/10nlu6c.pnghttps://pic.flymc.cc/i/hotlink-ok/2022/07/20/10nfc13.png
写入的数据定义在这里,PCTSTR szFileData = TEXT("这是一个例子。");
这个字符串有8个字符。我特意用sizeof(szFileData)计算了长度,并打印出来了。
结果显示8,但文本文件只显示4个字符。
我经过排查,发现是WriteFile函数的第3个参数,不能用sizeof(szFileData),而应该用lstrlen(szFileData)获得的长度。这样才能正确写入。
sizeof(szFileData)和lstrlen(szFileData)得出的长度居然很不一样。一个是14,一个是8。
请问大佬们,为什么WriteFile函数的第三个参数不能用sizeof(szFileData),而要用lstrlen(szFileData)。谢谢。
https://pic.flymc.cc/i/hotlink-ok/2022/07/20/10qzslt.pnghttps://pic.flymc.cc/i/hotlink-ok/2022/07/20/10qzs2w.png 本帖最后由 田田爱崽崽 于 2022-7-20 22:32 编辑
一个汉语字符是占两字节呀,没有毛病呀 田田爱崽崽 发表于 2022-7-20 22:30
一个汉语字符是占两字节呀,没有毛病呀
谢谢
洒家好像明白一点点了
PCTSTR szFileData = TEXT("这是一个例子。");
lstrlen(szFileData) = 14
去除掉最后的\0所占的两个字节,刚好是14字节。
而sizeof(szFileData)获取的是指针的长度,因为洒家是64位系统,所以是8字节
洒家误以为sizeof(szFileData)是获取字符数组的长度
刚好PCTSTR szFileData = TEXT("这是一个例子。");,也是8个字符
本帖最后由 爱飞的猫 于 2022-7-21 04:24 编辑
补充一下:
`strlen` 取出来的不是字符串的字符数量是从给定指针开始到 00 结束时的字节数量。
> strlen interprets the string as a single-byte character string, so its return value is always equal to the number of bytes, even if the string contains multibyte characters.
另外如果是 Unicode 字符串,应使用 `wcslen(pStr) * sizeof(wchar_t)` 得出字节数量。 sizeof(指针);在32位系统下返回4;在64位系统下返回8;
应该用 strlen() 函数来获取字符串指针的长度; 本帖最后由 mxwawaawxm 于 2022-7-21 09:37 编辑
爱飞的猫 发表于 2022-7-21 04:22
补充一下:
`strlen` 取出来的不是字符串的字符数量是从给定指针开始到 00 结束时的字节数量。
谢谢大佬。
PCTSTR szFileData = TEXT("这是一个例子。");
这样定义了字符指针后,获取字符串的长度
这么写 lstrlen(szFileData) * sizeof(TCHAR)
这样更准确,是吗?
如果是Unicode 字符串,自动转换成wcslen(pStr) * sizeof(wchar_t)
如果是ANSI字符串,转换成strlen(pStr) * sizeof(char) 本帖最后由 爱飞的猫 于 2022-7-21 16:32 编辑
mxwawaawxm 发表于 2022-7-21 09:36
谢谢大佬。
PCTSTR szFileData = TEXT("这是一个例子。");
这样定义了字符指针后,获取字符串的长度
对的,这个 API 的选择是编译时决定的。
`tchar.h` 提供的对应函数其实是 `_tcslen`。你能够在头文件看到这个函数根据 `_UNICODE` 是否定义而选择 `strlen` 或 `wcslen`。
这个函数明确说明了在 ANSI 的情况下是一个字节一个字节来数 CHAR 数量(`strlen` interprets the string as a single-byte character string, so its return value is always equal to the number of bytes, even if the string contains multibyte characters),而 Unicode 的情况下数 WCHAR 数量。
`lstrlen` 的文档未对此进行说明,只是提到了返回 `character` 的数量;但实际测试的结果也表明它与 `strlen` 的结果一致。
需要注意「字节数量」与「字符数量」,因为 `strlen` 会将「中文」将算作 4 个字节,而 `_mbslen` 函数会将其算作 2 个字符。
```c
#include <Windows.h> // 提供 lstrlen
#include <stdio.h>
#include <tchar.h>
#include <mbstring.h>
#include <locale.h>
int main()
{
// 注意返回的是 size_t,在 32/64 位下分别是 u32/u64。
printf("strlen(中文) = %zu\n", strlen("中文"));
printf("wcslen(中文) = %zu\n", wcslen(L"中文"));
printf("tcslen(中文) * sizeof(TCHAR) = %zu\n", _tcslen(TEXT("中文")) * sizeof(TCHAR));
// 数"文字字符"数量
_locale_t locale = _create_locale(LC_ALL, "zh-CN");
printf("_mbslen_l(中文, zh-CN) = %zu\n", _mbslen_l(reinterpret_cast<const unsigned char*>("中文"), locale));
printf("_mbslen(中文) = %zu\n", _mbslen(reinterpret_cast<const unsigned char*>("中文")));
// 返回的是 32 位的 int
printf("lstrlenA(中文) = %d\n", lstrlenA("中文"));
printf("lstrlenW(中文) = %d\n", lstrlenW(L"中文"));
}
```
```text
strlen(中文) = 4
wcslen(中文) = 2
tcslen(中文) * sizeof(TCHAR) = 4
_mbslen_l(中文, zh-CN) = 2
_mbslen(中文) = 2
lstrlenA(中文) = 4
lstrlenW(中文) = 2
```
相关文档:
- https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-lstrlena
- https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strlen-wcslen-mbslen-mbslen-l-mbstrlen-mbstrlen-l?view=msvc-170#generic-text-routine-mappings 爱飞的猫 发表于 2022-7-21 16:09
对的,这个 API 的选择是编译时决定的。
`tchar.h` 提供的对应函数其实是 `_tcslen`。你能够在头 ...
谢谢大佬。
我这边%zu不能正确打印_tcslen等函数获取的长度
总是提示
unknown conversion type character 'z' in format [-Wformat=]
我都得换成%I64u,才能正确打印size_t类型(其实就是long long unsigned int)的数值
另外中间reinterpret_cast,好像是C++的语法。C语言不是直接这样就可以强制类型转换了。
printf("_mbslen(中文) = %I64u\n", _mbslen((const unsigned char *)("中文")));
mxwawaawxm 发表于 2022-7-21 22:18
谢谢大佬。
我这边%zu不能正确打印_tcslen等函数获取的长度
总是提示
感觉可能和 vs 版本有关,我用的是 vs2022… 也有可能 c 使用编译器有一些不同?
reinterprete_cast 确实是 c++ 的,我给忘了… 爱飞的猫 发表于 2022-7-21 22:20
感觉可能和 vs 版本有关,我用的是 vs2022… 也有可能 c 使用编译器有一些不同?
reinterprete_cast...
大佬,请问下,关于wsprintf这个函数,tchar.h对应提供的函数是什么?
感觉wsprintf和sprintf函数差不多(两个函数应该的区别是:单字节/双字节字符)
但又怎么还设置了wsprintfA(应该是针对ANSI)和wsprintfW函数(针对宽字符串)。
页:
[1]
2