将 unicode sf::String 传递到 std::filesystem::u8path

Passing unicode sf::String into std::filesystem::u8path

我试图让 sf::String 变成 std::filesystem::u8path。我的第一个方法是将它转换成 std::string(std::string)sfstringbar 但它把它看作是一个单字节字符,我也试过 auto x = sfstringbar.toUtf8() std::string(x.begin(), x.end()) 但一样。我的第二种方法是将它作为 char 数组传递,希望可以使用 UTF 8 编码读取它,但仍然发生同样的情况。

编辑:

char* makeutf8str(str string) {
    std::basic_string<sf::Uint8> utf8 = string.toUtf8();
    std::vector<char>* out = new std::vector<char>;
    for (auto x = utf8.begin(); x != utf8.end(); x++) {
        out->push_back(*x);
    }
    return &(out->at(0));
}

bool neaxfile::isfile(str file) {
    std::cout << "\nThis: " << makeutf8str(file) << "\n";
    return std::filesystem::is_regular_file(std::filesystem::u8path(makeutf8str(file)));
}

这是我尝试的第二种解决方案。我有一个名为 Яyes.txt 的文件作为示例,但是当我传入以检查它是否存在时,它说它不存在。因为 makeutf8str() 函数将 Я 拆分为 Я。我似乎无法让编码器正常工作。

编辑 2:

str neaxfile::getcwd() {
    std::error_code ec;
    str path = std::filesystem::current_path(ec).u8string();
    if (ec.value() == 0) {
        return path;
    } else {
        return '[=13=]';
    }
}

std::vector<str> neaxfile::listfiles() {
    std::vector<str> res;
    for (auto entry : std::filesystem::directory_iterator((std::string)neaxfile::getcwd())) {
        if (neaxfile::isfile(entry.path().wstring())) res.push_back(entry.path().wstring());
    }
    return res;
}

我尝试了下面的第一个解决方案。它不再打印 Я。但它仍然不能确认这是一个文件。我尝试使用 ^

列出文件

std::filesystem::u8path() "从 chars [或 char8_ts (C++20 起)] 的 UTF-8 编码序列构建路径 p], 作为 std::stringstd::string_view 提供,或作为空终止多字节字符串,或作为 [first, last) 迭代器对提供。" =73=]

A std::string 可以保存 UTF-8 编码的字符序列(不过在 C++20 中最好使用 std::u8string)。 sf::String::ToUtf8() return 是 UTF-8 编码 std::basic_string<Uint8>。你可以简单地将 UInt8 数据转换为 char 来构造一个 std::string,你的 makeutf8str() 函数不需要使用 std::vector<char> 或 return 一个原始的 char*(特别是因为它正在泄漏 std::vector)。

您可以使用 std::string 构造函数,它以 char*size_t 作为输入,例如:

std::string makeutf8str(const str &string) {
    auto utf8 = string.toUtf8();
    return std::string(reinterpret_cast<const char*>(utf8.c_str()), utf8.size());
}

或者,您可以使用 std::string 构造函数,它将一系列迭代器作为输入(尽管您声称,这应该可以正常工作),例如:

std::string makeutf8str(const str &string) {
    auto utf8 = string.toUtf8();
    return std::string(utf8.begin(), utf8.end());
}

任何一种方式都适用于 std::coutstd::filesystem::u8path(),例如:

bool neaxfile::isfile(const str &file) {
    auto utf8 = makeutf8str(file);
    std::cout << "\nThis: " << utf8 << "\n";
    return std::filesystem::is_regular_file(std::filesystem::u8path(utf8));
}

也就是说,Unicode 字符 Я 在 UTF-8 中编码为字节 0xD0 0xAF,当 解释 为 Latin-1 而不是UTF-8 将显示为 Я。这意味着 std::string 数据是正确的 UTF-8 编码,只是没有被正确处理。例如,如果您的控制台无法处理 UTF-8 输出,那么您将看到 Я 而不是 Я。但是,u8path() 应该可以很好地处理 UTF-8 编码的 std::string,并根据需要将其转换为文件系统的本机编码。但是,不能保证底层文件系统实际上会正确处理像 Яyes.txt 这样的 Unicode 文件名,但这将是一个 OS 问题,而不是 C++ 问题。


更新:您的 listfiles() 函数在使用 directory_iterator 时根本没有使用 UTF-8。它将 sf::Stringgetcwd() 类型转换为 ANSI 编码的 std::string (这是一个 有损 转换),而不是 UTF-8编码 std::string。但更糟糕的是,sf::String 是由 getcwd() 从 UTF-8 编码的 std::string 构造的,但是 sf::Stringstd::string 构造函数默认需要 ANSI,而不是 UTF -8(要解决这个问题,你必须给它一个 UTF-8 std::locale)。因此,您正在通过几次有损转换,试图从 std::filesystem::pathreturned fromstd::filesystem::current_pathtostd::filesystem::directory_iterator`.

sf::String可以转换to/fromstd::wstringstd::filesystem::path也可以用,不用经过UTF-8和std::filesystem::u8path()至少,至少在 Windows 上,其中 std::wstring 使用 UTF-16,而 Windows 底层文件系统 API 也使用 UTF-16。

试试这个:

bool neaxfile::isfile(const str &file) {
    std::wstring wstr = file;
    std::wcout << L"\nThis: " << wstr << L"\n";
    return std::filesystem::is_regular_file(std::filesystem::path(wstr));
}

str neaxfile::getcwd() {
    std::error_code ec;
    str path = std::filesystem::current_path(ec).wstring();
    if (ec.value() == 0) {
        return path;
    } else {
        return L"";
    }
}

std::vector<str> neaxfile::listfiles() {
    std::vector<str> res;
    std::filesystem::path cwdpath(neaxfile::getcwd().wstring());
    for (auto entry : std::filesystem::directory_iterator(cwdpath) {
        str filepath = entry.path().wstring();
        if (neaxfile::isfile(filepath)) res.push_back(filepath);
    }
    return res;
}

如果您真的想使用 UTF-8 进行 C++ 字符串和 SFML 字符串之间的转换,请尝试这样做以避免任何数据丢失:

std::string makeutf8str(const str &string) {
    auto utf8 = string.toUtf8();
    return std::string(reinterpret_cast<const char*>(utf8.c_str()), utf8.size());
}

str fromutf8str(const std::string &string) {
    return str::fromUtf8(utf8.begin(), utf8.end());
}

bool neaxfile::isfile(const str &file) {
    auto utf8 = makeutf8str(file);
    std::cout << "\nThis: " << utf8 << "\n";
    return std::filesystem::is_regular_file(std::filesystem::u8path(utf8));
}

str neaxfile::getcwd() {
    std::error_code ec;
    auto path = std::filesystem::current_path(ec).u8string();
    if (ec.value() == 0) {
        return fromutf8str(path);
    } else {
        return "";
    }
}

std::vector<str> neaxfile::listfiles() {
    std::vector<str> res;
    auto cwdpath = std::filesystem::u8path(makeutf8str(neaxfile::getcwd()));
    for (auto entry : std::filesystem::directory_iterator(cwdpath)) {
        str filepath = fromutf8str(entry.path().u8string());
        if (neaxfile::isfile(filepath)) res.push_back(filepath);
    }
    return res;
}

也就是说,您在 C++ 字符串和 SFML 字符串之间进行了大量不必要的转换。当您不直接 与 SFML 的 API 交互时,您真的不应该使用 SFML 字符串。你真的应该尽可能多地使用 C++ 字符串,尤其是 <filesystem> API,例如:

bool neaxfile::isfile(const std::string &file) {
    std::cout << L"\nThis: " << file << L"\n";
    return std::filesystem::is_regular_file(std::filesystem::u8path(file));
}

std::string neaxfile::getcwd() {
    std::error_code ec;
    std::string path = std::filesystem::current_path(ec).u8string();
    if (ec.value() == 0) {
        return path;
    } else {
        return "";
    }
}

std::vector<std::string> neaxfile::listfiles() {
    std::vector<std::string> res;
    auto cwdpath = std::filesystem::u8path(neaxfile::getcwd());
    for (auto entry : std::filesystem::directory_iterator(cwdpath)) {
        auto filepath = entry.path().u8string();
        if (neaxfile::isfile(filepath)) res.push_back(filepath);
    }
    return res;
}

或者:

bool neaxfile::isfile(const std::wstring &file) {
    std::wcout << L"\nThis: " << file << L"\n";
    return std::filesystem::is_regular_file(std::filesystem::path(file));
}

std::wstring neaxfile::getcwd() {
    std::error_code ec;
    auto path = std::filesystem::current_path(ec).wstring();
    if (ec.value() == 0) {
        return path;
    } else {
        return L"";
    }
}

std::vector<std::wstring> neaxfile::listfiles() {
    std::vector<std::wstring> res;
    std::filesystem::path cwdpath(neaxfile::getcwd());
    for (auto entry : std::filesystem::directory_iterator(cwdpath)) {
        auto filepath = entry.path().wstring();
        if (neaxfile::isfile(filepath)) res.push_back(filepath);
    }
    return res;
}

更好的选择是根本不传递字符串。 std::filesystem::path 是一种抽象,可以帮助您避免这种情况,例如:

bool neaxfile::isfile(const std::filesystem::path &file) {
    std::wcout << L"\nThis: " << file.wstring() << L"\n";
    return std::filesystem::is_regular_file(file);
}

std::filesystem::path neaxfile::getcwd() {
    std::error_code ec;
    auto path = std::filesystem::current_path(ec);
    if (ec.value() == 0) {
        return path;
    } else {
        return {};
    }
}

std::vector<std::filesystem::path> neaxfile::listfiles() {
    std::vector<std::filesystem::path> res;
    for (auto entry : std::filesystem::directory_iterator(neaxfile::getcwd())) {
        auto filepath = entry.path();
        if (neaxfile::isfile(filepath)) res.push_back(filepath);
    }
    return res;
}