c++ - 将 unicode sf::String 传递到 std::filesystem::u8path

标签 c++ filesystems c++17 sfml

我试图得到 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 '\0';
    }
}

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() "从 p s [或 char s (C++20 起)] 的 UTF-8 编码序列构造路径 char8_t 作为 std::stringstd::string_view 提供,或者作为一个以空字符结尾的多字节字符串,或者作为一个 [first, last) 迭代器对。"
一个 std::string可以保存一个 UTF-8 编码的字符序列(最好在 C++20 中使用 std::u8string)。 sf::String::ToUtf8() 返回一个 UTF-8 编码的 std::basic_string<Uint8> .您可以简单地转换 UInt8数据到 char构建 std::string ,不需要你的makeutf8str()使用函数std::vector<char>或返回原始 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很好,并根据需要将其转换为文件系统的 native 编码。但是,不能保证底层文件系统实际上会处理像 Яyes.txt 这样的 Unicode 文件名。正确,但这将是操作系统问题,而不是 C++ 问题。

更新 : 你的 listfiles()函数在使用 directory_iterator 时根本没有使用 UTF-8 .它是 sf::String 的类型转换来自 getcwd() ANSI 编码 std::string (这是有损转换),而不是 UTF-8 编码的 std::string .但更糟糕的是,sf::Stringgetcwd() build 来自 UTF-8 编码的 std::string但是 std::string sf::String 的构造函数默认情况下需要 ANSI,而不是 UTF-8(要解决这个问题,你必须给它一个 UTF-8 std::locale )。因此,您正在经历几次有损转换,试图从 std::filesystem::path returned from 获取字符串。 std::filesystem::current_path to std::filesystem::directory_iterator`。sf::String可以转换到/从 std::wstring , 其中 std::filesystem::path也可以使用,所以不用经过UTF-8和std::filesystem::u8path()至少在 std::wstring 的 Windows 上使用 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;
}

关于c++ - 将 unicode sf::String 传递到 std::filesystem::u8path,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63175473/

相关文章:

c++ - 我怎么能只使用一次cin?

filesystems - 创建集群文件服务器系统

c++ - 我已经编写了 C++ 代码来将 boost::optional 视为一个范围,但它无法编译

C++函数中的广义二维数组

javascript - 无法将数据写入 Node JS 中的文件

c# - 无法使用 FileSystemWatcher 监视 word 文档

c++ - 如何有效应对需要所有权的临时 helper ?

c++ - 为什么在使用 libc++ 时 sizeof( std::variant< char > ) == 8 而不是 2 (如 MSVC 的 STL 和 libstdc++)?

用于方法组和装饰器的 C++ SFINAE

c++ - 如何使用自定义错误消息引发错误?