写入包含在C:\ Program Files中的文件夹的文件名

时间:2013-10-26 09:18:56

标签: c++ winapi unicode

我有这个任务:
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)将作为“N e w T o”(strlen = 10)写入文件。怎么办?

5 个答案:

答案 0 :(得分:2)

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

    cout << "CreateFile() succeeded!\n";
    wchar_t bom = L'\xfeff';
    WriteFile(c, &bom, sizeof(bom), &bytesWritten, NULL);

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

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

UTF-8具有更紧凑的优势。它还与纯ASCII完全兼容,并受到编程社区的青睐。

如果您不需要在任何上下文中使用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

答案 3 :(得分:0)

你应该知道在windows中,“native”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)

答案 4 :(得分: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;
}