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::stringcoutcin 的宽字符版本? 对我来说,您的代码有效。 (甚至是第一个版本) @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 字符的主要内容,如果未能解决你的问题,请参考以下文章

string 类型的输入操作符 cin 和 getline 函数分别如何处理空白字符?

C++ 中 cin.getline() 的意外返回

关于scanf 与 cin gets(),getline()......输入输出字符串的区别

cin.get() 和 cin.getline() 之间的区别

使用 cin.getline() 和 cin.get()

std::cin.getline() 与 std::cin