mxwawaawxm 发表于 2022-7-20 22:24

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:30

本帖最后由 田田爱崽崽 于 2022-7-20 22:32 编辑

一个汉语字符是占两字节呀,没有毛病呀

mxwawaawxm 发表于 2022-7-20 22:45

田田爱崽崽 发表于 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:22

本帖最后由 爱飞的猫 于 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)` 得出字节数量。

忘了带钱 发表于 2022-7-21 09:35

sizeof(指针);在32位系统下返回4;在64位系统下返回8;
应该用 strlen() 函数来获取字符串指针的长度;

mxwawaawxm 发表于 2022-7-21 09:36

本帖最后由 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:09

本帖最后由 爱飞的猫 于 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

mxwawaawxm 发表于 2022-7-21 22:18

爱飞的猫 发表于 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 *)("中文")));

爱飞的猫 发表于 2022-7-21 22:20

mxwawaawxm 发表于 2022-7-21 22:18
谢谢大佬。
我这边%zu不能正确打印_tcslen等函数获取的长度
总是提示


感觉可能和 vs 版本有关,我用的是 vs2022… 也有可能 c 使用编译器有一些不同?

reinterprete_cast 确实是 c++ 的,我给忘了…

mxwawaawxm 发表于 2022-7-27 21:55

爱飞的猫 发表于 2022-7-21 22:20
感觉可能和 vs 版本有关,我用的是 vs2022… 也有可能 c 使用编译器有一些不同?

reinterprete_cast...

大佬,请问下,关于wsprintf这个函数,tchar.h对应提供的函数是什么?
感觉wsprintf和sprintf函数差不多(两个函数应该的区别是:单字节/双字节字符)
但又怎么还设置了wsprintfA(应该是针对ANSI)和wsprintfW函数(针对宽字符串)。
页: [1] 2
查看完整版本: C语言调用WriteFile函数向txt文件写入8个中文字符串,但txt文件却显示4个