无法在C ++中读取带有西里尔语路径的文件

时间:2017-04-14 05:53:29

标签: c++ windows io fstream ifstream

我正在尝试读取其路径中包含西里尔字符的文件,并获得ifstream.is_open() == false 这是我的代码:

std::string ReadFile(const std::string &path) {
    std::string newLine, fileContent;
    std::ifstream in(path.c_str(), std::ios::in);

    if (!in.is_open()) {
        return std::string("isn't opened");
    }

    while (in.good()) {
        getline(in, newLine);
        fileContent += newLine;
    }

    in.close();

    return fileContent;
}

int main() {
    std::string path = "C:\\test\\документ.txt";
    std::string content = ReadFile(path);
    std::cout << content << std::endl;
    return 0;
}

指定的文件存在

我正试图在谷歌找到解决方案,但我什么都没得到

这是链接,我看到了:

I don't need wstring

The same as previous

no answer here

is not about C++

has no answer too

P.S。我需要在string中获取文件内容,而不是在wstring

这是编码我的IDE的设置(CLION 2017.1)

My encoding

4 个答案:

答案 0 :(得分:1)

您需要一个最新的编译器或Boost。 std::filesystem::path可以处理这些名称,但它是C ++ 17标准中的新名称。您的编译器可能仍然将其作为std::experimental::filesystem::path,否则您将使用第三方boost::filesystem::path。界面非常具有可比性,因为Boost版本是灵感。

答案 1 :(得分:0)

std :: string的定义是std :: basic_string,因此您的Cyrillic chararecters不会按预期存储。至少,尝试使用std :: wstring存储文件路径,然后使用std :: string从文件中读取。

答案 2 :(得分:0)

首先,将项目设置设置为使用UTF-8编码而不是windows-1251。直到标准库变得非常好(不是很快),如果你想正确处理io,你基本上不能依赖它。要在Windows上从文件中读取输入流,您需要编写自己的自定义输入流缓冲区,使用2字节宽字符打开文件或依赖此类例程的某些第三方实现。以下是一些不完整的(但对于您的示例来说足够)实现:

// assuming that usual Windows SDK macros such as _UNICODE, WIN32_LEAN_AND_MEAN are defined above
#include <Windows.h>

#include <string>
#include <iostream>
#include <system_error>
#include <memory>
#include <utility>
#include <cstdlib>
#include <cstdio>

static_assert(2 == sizeof(wchar_t), "wchar_t size must be 2 bytes");

using namespace ::std;

class MyStreamBuf final: public streambuf
{
    #pragma region Fields
    private: ::HANDLE const  m_file_handle;
    private: char            m_buffer; // typically buffer should be much bigger
    #pragma endregion

    public: explicit
    MyStreamBuf(wchar_t const * psz_file_path)
    :   m_file_handle(::CreateFileW(psz_file_path, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))
    ,   m_buffer{}
    {
        if(INVALID_HANDLE_VALUE == m_file_handle)
        {
            auto const error_code{::GetLastError()};
            throw(system_error(static_cast< int >(error_code), system_category(), "::CreateFileW call failed"));
        }
    }

    public:
    ~MyStreamBuf(void)
    {
        auto const closed{::CloseHandle(m_file_handle)};
        if(FALSE == closed)
        {
            auto const error_code{::GetLastError()};
            //throw(::std::system_error(static_cast< int >(error_code), system_category(), "::CloseHandle call failed"));
            // throwing in destructor is kinda wrong
            // but if CloseHandle returned false then our program is in inconsistent state
            // and must be terminated anyway
            (void) error_code; // not used
            abort();
        }
    }

    private: auto
    underflow(void) -> int_type override
    {
        ::DWORD bytes_count_to_read{1};
        ::DWORD read_bytes_count{};
        {
            auto const succeeded{::ReadFile(m_file_handle, addressof(m_buffer), bytes_count_to_read, addressof(read_bytes_count), nullptr)};
            if(FALSE == succeeded)
            {
                auto const error_code{::GetLastError()};
                setg(nullptr, nullptr, nullptr);
                throw(system_error(static_cast< int >(error_code), system_category(), "::ReadFile call failed"));
            }
        }
        if(0 == read_bytes_count)
        {
            setg(nullptr, nullptr, nullptr);
            return(EOF);
        }
        setg(addressof(m_buffer), addressof(m_buffer), addressof(m_buffer) + 1);
        return(m_buffer);
    }
};

string
MyReadFile(wchar_t const * psz_file_path)
{
    istream in(new MyStreamBuf(psz_file_path)); // note that we create normal stream
    string new_line;
    string file_content;
    while(in.good())
    {
        getline(in, new_line);
        file_content += new_line;
    }
    return(::std::move(file_content));
}

int
main(void)
{
    string content = MyReadFile(L"C:\\test\\документ.txt"); // note that path is a wide string
    cout << content << endl;
    return 0;
}

答案 3 :(得分:0)

更改您的代码以使用wstring并使用Unicode编码保存您的文件(非UTF8,使用USC-2,UTF16或类似的东西)。 MSVC具有非标准的重载,因此能够处理文件名中的非ascii字符:

std::string ReadFile(const std::wstring &path)
{
    std::string newLine, fileContent;
    std::ifstream in(path.c_str(), std::ios::in);

    if (!in)
        return std::string("isn't opened");

    while (getline(in, newLine))
        fileContent += newLine;

    return fileContent;
}

int main()
{
    std::wstring path = L"C:\\test\\документ.txt";
    std::string content = ReadFile(path);
    std::cout << content << std::endl;
}

另请注意更正的ReadFile代码。