Cin 和 getline 不会正确保存非 ascii 字符
Posted
技术标签:
【中文标题】Cin 和 getline 不会正确保存非 ascii 字符【英文标题】:Cin and getline won't save non ascii characters correctly 【发布时间】:2019-11-18 14:37:20 【问题描述】:我正在尝试打开一个路径包含非 ascii 字符的文件。用户将文件拖到 cmd 中,我使用 getline 获取路径。 当我尝试使用用户提供的路径打开文件时,它不起作用。
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;
int main()
string userInput; //Saves the user input
string autoInput = "C:\\Espolón\\file.txt"; //Specifying the path like this works
ifstream file1; //For opening the file with the userInput
ifstream file2; //For opening the file with autoInput
getline(cin, userInput);
system("CLS"); //Clears the CMD
file1.open(userInput); //This throws an error. Note that I didn't use is_open for cleaner code but im actually using it in my tests
file2.open(autoInput); //This works perfectly
cout << "User input: " + userInput << endl<<"Auto input: " + autoInput << endl; //Both show correctly in the CMD
system("pause");
虽然 cout 正确显示了所有内容,但在调试时我发现 userInput 非 ascii 字符 'ó' 正在更改为 '¢' ("C:\Espol¢n\file.txt") 但自动输入是正确存储(“C:\Espolón\file.txt”): screen capture. 因此,如果文件有特殊字符,我无法使用用户提供的路径打开文件。
我尝试使用在其他类似问题中读到的宽字符:
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;
int main()
wstring userInput; //Saves the user input
string autoInput = "C:\\Espolón\\file.txt"; //Specifying the path like this works
ifstream file1; //For opening the file with the userInput
ifstream file2; //For opening the file with a fix string
getline(wcin, userInput);
system("CLS"); //Clears the CMD
file1.open(userInput); //This throws an error. Note that I didn't use is_open for cleaner code but im actually using it in my tests
file2.open(autoInput); //This works perfectly
wcout << L"User input: " + userInput << endl;
cout<<"Auto input: " + autoInput << endl; //Both show correctly in the CMD
system("pause");
但问题仍然存在。 我也尝试将编码更改为 Unicode,但没有任何改变。
欢迎任何帮助! (对不起,英语不好)
【问题讨论】:
看std::locale
您是否尝试过使用std::string
、cout
和cin
的宽字符版本?
对我来说,您的代码有效。 (甚至是第一个版本)
@ThomasMatthews 是的!那是在第二个代码块中。
@n314159 您是否尝试过使用非 ascii 字符的路径?您使用的是哪个 IDE?我正在使用 Visual Studio Community 2019。
【参考方案1】:
Windows API 需要 UTF16LE 格式的 Unicode,而不是 UTF8。宽字符串字符和函数在 Windows 中被视为 UTF16。
字母 ó
是 ANSI,而不是 ASCII。 Windows 也支持 ANSI。如果您的程序在您自己的计算机上运行,您通常可以使用 ANSI,但即便如此,您也可能会遇到问题。如果您想使用 ANSI,请尽量不要将其与 UTF16 和宽字符串格式(std::wstring
)混合使用
Windows 8 和 10 对 UTF8 的控制台支持有限。但如果您想避免 UTF8 的陷阱,建议使用 UTF16。
这里是 UTF16 版本:
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
#include <io.h>
#include <fcntl.h>
using namespace std;
int main()
_setmode(_fileno(stdout), _O_U16TEXT);
_setmode(_fileno(stdin), _O_U16TEXT);
wstring userInput;
wstring autoInput = L"C:\\Espolón\\file.txt";
ifstream file1, file2;
getline(wcin, userInput);
file1.open(userInput);
file2.open(autoInput);
wcout << L"file1: " << (file1.good() ? L"good\n" : L"bad\n");
wcout << L"file2: " << (file1.good() ? L"good\n" : L"bad\n");
wcout << L"User input: " + userInput << endl;
wcout << L"Auto input: " + autoInput << endl;
system("pause");
return 0;
基于 Unix 的系统使用 UTF8。请注意,这些系统不容易与 UTF16 相处。他们使用
std::wstring
用于UTF32。在大多数情况下,您可以简单地将std::string
用于 Linux、Mac 等中的 UTF8。
文本文件可能包含 ASCII 格式、ANSI、UTF8 或 UTF16 的文本。建议使用 UTF8 兼容。
在 Windows 中,以 UTF8 格式存储文本文件。使用这些函数在 UTF8 和 UTF16 之间来回转换:
//need at least c++11 for writable std::string::data()
std::string get_u8(const std::wstring u16)
if(u16.empty()) return std::string();
int size = WideCharToMultiByte(CP_UTF8, 0, u16.c_str(), -1, 0, 0, 0, 0);
std::string u8(size, 0);
WideCharToMultiByte(CP_UTF8, 0, u16.c_str(), -1, u8.data(), size, 0, 0);
return u8;
std::wstring get_u16(const std::string u8)
if(u8.empty()) return std::wstring();
int size = MultiByteToWideChar(CP_UTF8, 0, u8.c_str(), -1, 0, 0);
std::wstring u16(size, 0);
MultiByteToWideChar(CP_UTF8, 0, u8.c_str(), -1, u16.data(), size);
return u16;
【讨论】:
有效!感谢您提供完整且内容丰富的答案:) 不客气,但我的回答远未完成。当涉及到 Windows 和 Unicode 时,这个话题有点混乱。查看更新的答案和其他资源。以上是关于Cin 和 getline 不会正确保存非 ascii 字符的主要内容,如果未能解决你的问题,请参考以下文章
关于scanf 与 cin gets(),getline()......输入输出字符串的区别