1

我有这个任务:
1. 在当前目录中创建文件 subMape.dat
2. 将存储在 C:\Program Files 文件夹中的所有文件夹名称写入其中
3. 在屏幕上显示数据,写入 subMape.dat

#include <iostream>
#include <windows.h>

using namespace std;

int main() {
    WIN32_FIND_DATA findFileData;
    DWORD bytesWritten = 0;

    HANDLE f;
    HANDLE c = CreateFileW(L"subMape.txt", GENERIC_READ | GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    //TCHAR lpBuffer[32];
    DWORD nNumberOfBytesToRead = 32;
    //DWORD lpNumberOfBytesRead;

    DWORD lengthSum = 0;

    if (c) {
        cout << "CreateFile() succeeded!\n";
        if(f = FindFirstFile(L"C:\\Program Files\\*", &findFileData)){ 
            if(f != INVALID_HANDLE_VALUE) {

                while (FindNextFile(f, &findFileData)){
                    lengthSum += bytesWritten;
                    WriteFile(c, findFileData.cFileName, (DWORD)wcslen(findFileData.cFileName), &bytesWritten, NULL);       
                }
            }
            FindClose(f);
        }

        else {
            cout << "FindFirstFile() failed :(\n";
        }

    }

    else {
        cout << "CreateFile() failed :(\n";
    }
    cout << lengthSum << endl;
    //SetFilePointer(c, lengthSum, NULL, FILE_BEGIN);
    //ReadFile(c, lpBuffer, lengthSum, &lpNumberOfBytesRead, NULL);
    //wprintf(lpBuffer);

    CloseHandle(c);

    return 0;
}

我正在使用 UNICODE,当它写入 findFileData.cFileName - 它写入字符串,其中字符用空格分隔。例如:文件夹名称“New Folder”(strlen = 10)将作为“New To”(strlen = 10)写入文件。做什么?

4

5 回答 5

2

您的文本文件查看器或编辑器不够聪明,无法确定您编写了一个 utf-16 编码的文本文件。大多数文本编辑器都需要帮助,将 BOM写入文件:

    cout << "CreateFile() succeeded!\n";
    wchar_t bom = L'\xfeff';
    WriteFile(c, &bom, sizeof(bom), &bytesWritten, NULL);
于 2013-10-26T09:59:26.063 回答
1

您看到“空格”的原因是您用来列出文件的程序将其视为每个字符一个字节。在 Windows 中使用 Unicode 时,你会得到两个,第二个字节是 '\0'。

您需要选择如何对文件中的数据进行编码。

最简单的方法是使用UTF-16LE,因为这是 Windows 上的本机编码。然后您只需要在文件开头添加一个字节顺序标记。这种编码具有一个优势,因为由于观察到的零字节,UTF-8它很容易与编码区分开来。extended ASCII它的缺点是你需要它BOM并且它占用更多未压缩的磁盘空间。

UTF-8具有更紧凑的优点。它也完全兼容pureASCII并受到编程界的青睐。

如果您不需要extended ASCII在任何上下文中使用,则应将数据编码为UTF-8. 如果这样做,请使用UTF-16LE.

那些认为通过UTF-8验证的文本被编码的人在UTF-8整个文本可用时是正确的,但如果不是,则错误:

考虑一个按字母顺序排列的瑞典名字列表。如果我只检查列表的第一部分并且它是Latin-1ISO/IEC 8859-1),它也将通过UTF-8测试。

最后是“Örjansson”,它分解成 mojibake 事实上,“Ö”将是一个无效的UTF-8位序列。另一方面,由于使用时所有使用的字母实际上都适合一个字节UTF-16LE,所以我可以完全确信它不是UTF-8,也不Latin-1是。

于 2013-10-26T10:08:22.197 回答
1

您需要使用类似的东西WideCharToMultiByte()将 UNICODE 字符串转换为 ANSI(或 UTF8)。

于 2013-10-26T09:39:44.137 回答
0

在处理 UTF-16 文件时,写入字节顺序标记并以字节而不是字符的长度写入数据非常重要。 wcslen返回字符串长度(以字符为单位),但在使用宽字符串时,一个字符为两个字节。这是一个固定版本。它显式调用 Win32 API 的宽版本,因此无论是否定义了 UNICODE/_UNICODE 都可以工作。

#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
    WIN32_FIND_DATAW findFileData; // Use the wide version explicitly
    DWORD bytesWritten = 0;

    HANDLE f;
    HANDLE c = CreateFileW(L"subMape.txt", GENERIC_READ | GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    DWORD lengthSum = 0;

    if(c != INVALID_HANDLE_VALUE) {
        cout << "CreateFile() succeeded!\n";

        // Write A byte-order mark...make sure length is bytes not characters.
        WriteFile(c, L"\uFEFF", sizeof(wchar_t), &bytesWritten, NULL);
        lengthSum += bytesWritten;

        f = FindFirstFileW(L"C:\\Program Files\\*", &findFileData);
        if(f != INVALID_HANDLE_VALUE) {

            while(FindNextFileW(f, &findFileData)) {
                // Write filename...length in bytes
                WriteFile(c, findFileData.cFileName, (DWORD)wcslen(findFileData.cFileName) * sizeof(wchar_t), &bytesWritten, NULL);
                // Add the length *after* writing...
                lengthSum += bytesWritten;

                // Add a carriage return/line feed to make Notepad happy.
                WriteFile(c, L"\r\n", sizeof(wchar_t) * 2, &bytesWritten, NULL);
                lengthSum += bytesWritten;
            }
            FindClose(f); // This should be inside findFirstFile succeeded block.
        }
        else {
            cout << "FindFirstFile() failed :(\n";
        }

        // these should be inside CreateFile succeeded block.
        CloseHandle(c);
        cout << lengthSum << endl;
    }
    else {
        cout << "CreateFile() failed :(\n";
    }
    return 0;
}
于 2013-10-26T19:43:34.963 回答
0

您应该知道,在 Windows 中,“本机”uncidode 格式是 UTF-16,W 样式函数 ( CreateFileW ) 使用它。考虑到这一点,编写文件应该为您提供有效的 UTF-16 文本,但编辑器可能无法识别,为确保您的程序正常工作,请使用文本编辑器,您可以在其中手动指定编码(您知道它需要什么) 以防它无法识别,因为这个 Notepad++ 是一个不错的选择。

正如其他人已经提到的,编写 BOM 对文本编辑器非常有帮助,并确保您的文件将被正确读取。

您可以使用 WideCharToMultiByte 将 UTF-16 转换为 UTF-8 以获得更高的兼容性。

为什么你直接使用 CreateFileW 而不是 FindFirstFileW 你在你的项目中定义了 UNICODE 吗?如果您这样做,编译器会为您将 CreateFile 解析为 CreateFileW。

也在这里

WriteFile(c, findFileData.cFileName, (DWORD)wcslen(findFileData.cFileName), &bytesWritten, NULL);

wcslen 给出的字符数与非 ANSI 文本的数据大小不同,它应该类似于

wcslen(findFileData.cFileName)*sizeof(wchar_t)
于 2013-10-26T15:29:49.620 回答