将 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()
"从 char
s [或 char8_t
s (C++20 起)] 的 UTF-8 编码序列构建路径 p
], 作为 std::string
或 std::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::cout
和 std::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::String
从 getcwd()
类型转换为 ANSI 编码的 std::string
(这是一个 有损 转换),而不是 UTF-8编码 std::string
。但更糟糕的是,sf::String
是由 getcwd()
从 UTF-8 编码的 std::string
构造的,但是 sf::String
的 std::string
构造函数默认需要 ANSI,而不是 UTF -8(要解决这个问题,你必须给它一个 UTF-8 std::locale
)。因此,您正在通过几次有损转换,试图从 std::filesystem::pathreturned from
std::filesystem::current_pathto
std::filesystem::directory_iterator`.
sf::String
可以转换to/fromstd::wstring
,std::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;
}
我试图让 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()
"从 char
s [或 char8_t
s (C++20 起)] 的 UTF-8 编码序列构建路径 p
], 作为 std::string
或 std::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::cout
和 std::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::String
从 getcwd()
类型转换为 ANSI 编码的 std::string
(这是一个 有损 转换),而不是 UTF-8编码 std::string
。但更糟糕的是,sf::String
是由 getcwd()
从 UTF-8 编码的 std::string
构造的,但是 sf::String
的 std::string
构造函数默认需要 ANSI,而不是 UTF -8(要解决这个问题,你必须给它一个 UTF-8 std::locale
)。因此,您正在通过几次有损转换,试图从 std::filesystem::pathreturned from
std::filesystem::current_pathto
std::filesystem::directory_iterator`.
sf::String
可以转换to/fromstd::wstring
,std::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;
}