1

I would like to read a file from disk and during program execution using QDialog (Qt Widget) I choose the path to the file. Piece of code is very simple:

ifstream infile(path.c_str());

if (infile.is_open()) {
    //some code   
}
else
    //log it

The problem arises depending on the location of the target file:

  • If the path to the file is "D:\folder\file" - it reads the file and everything is okay
  • If the path to the file is "C:\Users\Rafał Surname\Desktop\folder\file" - file cannot be opened

How can this be solved ?

ukson
  • 125
  • 4
  • 10
  • _'file cannot be opened'_ You may want to use the [`std::ifstream::exceptions()`](http://en.cppreference.com/w/cpp/io/basic_ios/exceptions) member to get more information what exactly goes wrong. – πάντα ῥεῖ May 31 '14 at 10:52
  • @CodyGray: the answers there are very incomplete, and the selected solution is old and therefore includes information that is incorrect now: "there's no direct support for UTF-8 in the standard library". – Cheers and hth. - Alf May 31 '14 at 12:24

2 Answers2

10

At a guess, your machine's Windows ANSI (the codepage returned by GetACP) does not contain the character "ł" in your name.

The simplest solution is to not use such character in the path.

Otherwise you can use wide paths, wchar_t based paths. Visual C++ support them even though standard C++ does not.

Alternatively, to make the code work also with g++, you can use the Windows API to translate the wide path to a short path, where each name is maximum 8 characters. The short folder and file names are alternates intended precisely for compatibility with non-Unicode software. However, while that works nicely and easily for the case at hand, namely opening a file, it's more complicated for creating a file.

Finally, you can use Boost filesystem, which is very similar if not identical to the functionality that will become part of C++14. And apparently a version/variant is already part of the Visual C++ runtime library. A problem with that is, however, that it may not work with g++, since it relies on the aforementioned wide path support in Visual C++ (yes, it's almost a paradox, getting libraries with such requirements in the standard).


There is some low level Unicode path support in g++'s standard library implementation, but not anything like offering wchar_t based constuctors. I delved into some old code of mine and found the code below. cppx::Syschar is a type defined by my code, and it has wchar_t as underlying type in Windows:

File "open_file.h"
#pragma once

#include <stdio.h>      // fopen        !!!Needs to be first for g++ 4.7.2.
#include <rfc/cppx/core/Syschar.h>      // cppx::Syschar etc.
#include <wchar.h>      // _wfopen

namespace x {
    using cppx::Syschar;

    inline auto open_file( char const* const path, char const* const options )
        -> FILE*
    { return ::fopen( path, options ); }

    inline auto open_file( wchar_t const* const path, wchar_t const* const options )
        -> FILE*
    { return _wfopen( path, options ); }

    inline auto open_file( Syschar const* path, Syschar const* options )
        -> FILE*
    { return open_file( raw( path ), raw( options ) ); }
}  // namespace zx
File "ofstream.g++_compiler.h"
#pragma once
#include "open_file.h"                  // open_file
#include <ext/stdio_filebuf.h>          // __gnu_cxx::stdio_filebuf

namespace x {
    using cppx::Syschar;

    class ofstream
        : public std::ofstream
    {
    private:
        __gnu_cxx::stdio_filebuf<char>  buf_;
        std::streambuf*                 p_original_buf_;

    public:
        ~ofstream()
        {
            basic_ios::rdbuf( p_original_buf_ );
            buf_.close();
        }

        ofstream( Syschar const* const filename )
            : std::ofstream()
            , buf_(
                open_file( filename, CPPX_U( "w" ) ),
                std::ios_base::out
                )
            , p_original_buf_( nullptr )
        {
            p_original_buf_ = basic_ios::rdbuf( &buf_ );
        }
    };

}  // namespace x

Of course this is an ofstream, not an ifstream.

And again, it's just an example of the support in g++, while the answer to your question most likely involves either using the wide path support in Visual C++, or using the Windows API, or simply avoiding Unicode-specific characters, as discussed above.

Leveraging the Visual C++ wide path support a similar ofstream implementation can simply be like this:

File "ofstream.msvc_compiler.h"
#pragma once

#include <rfc/cppx/core/Syschar.h>      // cppx::Syschar etc.

#include <fstream>      // std::ofstream

namespace x {
    using cppx::Syschar;

    class ofstream
        : public std::ofstream
    {
    public:
        // Other stuff, and ...
        ofstream( Syschar const* const filename )
            : std::ofstream( raw( filename ) )   // wchar_t, a Visual C++ extension.
        {}
    };
}  // namespace x
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
0

That's because of that slanted "l" in your path. It's a Unicode character. There's a current problem with Unicode in paths. As far as I know, only open() method of streams in Visual Studio accepts wchar_t* paths.

Community
  • 1
  • 1
polkovnikov.ph
  • 6,256
  • 6
  • 44
  • 79