C++ 在C++;

C++ 在C++;,c++,file,C++,File,在我正在进行的项目中,我处理文件,并在继续之前检查它们是否存在。重命名甚至使用文件路径中带有“en-dash”的文件似乎是不可能的 std::string _old = "D:\\Folder\\This – by ABC.txt"; std::rename(_old.c_str(), "New.txt"); 这里_old变量被ABC.txt解释为D:\Folder\Thisā 我试过了 但是没有一个成功。。该怎么办?它确实依赖于平台,Unicode是一个令人头痛的问题。取决于您使用的编译器。

在我正在进行的项目中,我处理文件,并在继续之前检查它们是否存在。重命名甚至使用文件路径中带有“en-dash”的文件似乎是不可能的

std::string _old = "D:\\Folder\\This – by ABC.txt";
std::rename(_old.c_str(), "New.txt");
这里_old变量被ABC.txt解释为D:\Folder\Thisā 我试过了


但是没有一个成功。。该怎么办?

它确实依赖于平台,Unicode是一个令人头痛的问题。取决于您使用的编译器。对于MS(VS2010或更早版本)中的旧版本,您需要使用MSDN中描述的API。此测试示例使用您有问题的名称创建文件,然后重命名它

// #define _UNICODE // might be defined in project
#include <string>

#include <tchar.h>
#include <windows.h>

using namespace std;

// Convert a wide Unicode string to an UTF8 string
std::string utf8_encode(const std::wstring &wstr)
{
    if( wstr.empty() ) return std::string();
    int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
    std::string strTo( size_needed, 0 );
    WideCharToMultiByte                  (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
    return strTo;
}

// Convert an UTF8 string to a wide Unicode String
std::wstring utf8_decode(const std::string &str)
{
    if( str.empty() ) return std::wstring();
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo( size_needed, 0 );
    MultiByteToWideChar                  (CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

int _tmain(int argc, _TCHAR* argv[] ) {
    std::string pFileName = "C:\\This \xe2\x80\x93 by ABC.txt";
    std::wstring pwsFileName = utf8_decode(pFileName);

    // can use CreateFile id instead
    HANDLE hf = CreateFileW( pwsFileName.c_str() ,
                      GENERIC_READ | GENERIC_WRITE,
                      0,
                      0,
                      CREATE_NEW,
                      FILE_ATTRIBUTE_NORMAL,
                      0);
    CloseHandle(hf);
    MoveFileW(utf8_decode("C:\\This \xe2\x80\x93 by ABC.txt").c_str(), utf8_decode("C:\\This \xe2\x80\x93 by ABC 2.txt").c_str());
}
用于转换的字符大小有问题。。使用0作为目标缓冲区的大小调用WideChartMultiByte,可以获得转换所需的字符大小。然后,它将返回目标缓冲区大小所需的字节数。所有这些代码杂耍解释了为什么像Qt这样的框架有如此复杂的代码来支持基于Unicode的文件系统。实际上,对您来说,消除所有可能的bug的最经济有效的方法就是使用这样的框架

VS2015

std::string _old = u8"D:\\Folder\\This \xe2\x80\x93 by ABC.txt"s;
根据他们的文件。我查不到那个

为了明哥

std::string _old = u8"D:\\Folder\\This \xe2\x80\x93 by ABC.txt";
std::cout << _old.data();
std::string _old=u8“D:\\Folder\\This\xe2\x80\x93 by ABC.txt”;
std::cout编码采用Unicode n-dash,U+2013“-”,作为代码点150(十进制)。当您将其输出到具有活动代码页437、原始或兼容的控制台时,它将被解释为“ā”。因此,在字符串文字中有正确的代码页1252字符,因为

    >P>使用Visual C++,默认为Windows ANSI代码页,用于编码窄字符串文字,或

  • 您使用的是一个旧版本的g++,它不执行标准的强制转换和检查,而是直接通过机器传递窄字符字节,并且您的源代码编码为Windows ANSI Western(或兼容),或者

  • 一些我没想到的事情

对于前两种可能性中的任何一种

重命名
调用将起作用

<>我测试了它确实使用Visual C++工作。我周围没有一个旧版本的g++,但是我测试了它在版本5.1下工作。也就是说,我测试了文件是否真的重命名为
New.txt

// Source encoding: UTF-8
// Execution character set: Windows ANSI Western a.k.a. codepage 1252.
#include <stdio.h>      // rename
#include <stdlib.h>     // EXIT_SUCCESS, EXIT_FAILURE
#include <string>       // std::string
using namespace std;

auto main()
    -> int
{
    string const a = ".\\This – by ABC.txt";    // Literal encoded as CP 1252.
    return rename( a.c_str(), "New.txt" ) == 0? EXIT_SUCCESS : EXIT_FAILURE;
}
如果该代码页支持n-dash字符,则该代码将起作用

这种编码模型基于每个相关的主区域设置有一个版本的可执行文件(包括字符编码)


另一种方法是使用Unicode执行所有操作。这可以通过Boost文件系统进行移植,Boost文件系统将被采用到C++17的标准库中。或者,您可以使用Windows API,或Windows中标准库的事实上的标准扩展,即
\u rename

使用Visual C++ 2015:

实验文件系统模块的使用实例
// Source encoding: UTF-8
// Execution character set: irrelevant (everything's done in Unicode).
#include <stdlib.h>     // EXIT_SUCCESS, EXIT_FAILURE

#include <filesystem>   // In C++17 and later, or Visual C++ 2015 and later.
using namespace std::tr2::sys;

auto main()
    -> int
{
    path const old_path = L".\\This – by ABC.txt";    // Literal encoded as wide string.
    path const new_path = L"New.txt";
    try
    {
        rename( old_path, new_path );
        return EXIT_SUCCESS;
    }
    catch( ... )
    {}
    return EXIT_FAILURE;
}
//源编码:UTF-8
//执行字符集:无关(一切都是用Unicode完成的)。
#包括//退出成功,退出失败
包含/ /在C++ 17和后面,或Visual C++ 2015和以后。
使用名称空间std::tr2::sys;
自动主机()
->int
{
path const old_path=L“\\This–by ABC.txt”;//文字编码为宽字符串。
path const new_path=L“new.txt”;
尝试
{
重命名(旧路径、新路径);
返回退出成功;
}
捕获(…)
{}
返回退出失败;
}

要正确地为可移植代码执行此操作,可以使用Boost,也可以创建一个使用任何可用实现的包装头。

这取决于操作系统。在Linux中,文件名是简单的字节数组:忘记编码,只需重命名文件即可

但看起来您使用的是Windows,文件名实际上是一个以null结尾的字符串,包含16位字符。在这种情况下,最好的方法是使用
wstring
,而不是混淆编码

不要试图编写独立于平台的代码来解决特定于平台的问题。Windows使用Unicode作为文件名,因此您必须编写特定于平台的代码,而不是使用标准函数
rename


只需用ABC。txt“< /Code >和调用<代码> < WrNeNe>代码> < /P> >使用C++代码源中的连字符的Unicode或UTF-8十六进制常数。你依赖C++代码编辑器来找出代码< > />代码>,所以如果编译器解释了这个字符,那么所有的“设置”调用对你来说都无关紧要。如果你用<代码> > \xE2\x80\x93> (UTF8)来替换<代码> ->代码>?你有没有尝试过“代码> U8”这一点——通过ABC?TXT“< /代码>?我试过了,但我得到了那些奇怪的符号,在Windows中不能有UTF-8语言环境,所以UT8编码的文件名将是胡言乱语。−1,因为这种方法将不匹配文件名作为源文件,或者将产生一个乱七八糟的文件名作为结果(在我注意到这一点后,答案还没有确定)。@alf你确定吗?NTFS以Unicode格式存储名称。事实上,作为一名俄罗斯人,我经常遇到这样的问题——用俄语命名文件会在NTFS卷上产生utf-8文件名。顺便说一下,我在widechar表格中没有找到en dash。它将它们存储在UTF-16中,正如我所说的,您需要API来转换它。显然是在引擎盖下做的?它不适用于fopen和其他POSIX函数,它们只适用于ANSIThe
rename
操作,OP使用的操作(实际上)是作为对Windows ANSI兼容层的调用实现的。也就是说,它需要Windows ANSI编码的字符串。UTF-8编码字符串,或任何支持n-dash的基于
char
的编码,如果有需要该编码的重命名操作,则可以,但Microsoft没有提供。你没有听到我说的,是吗?。不要在带有Unicode的Windows上使用文件访问POSIX函数。使用使用WCHAR的winapi。不是他们的“POSIX”层t
// Source encoding: UTF-8
// Execution character set: Windows ANSI Western a.k.a. codepage 1252.
#include <stdio.h>      // rename
#include <stdlib.h>     // EXIT_SUCCESS, EXIT_FAILURE
#include <string>       // std::string
using namespace std;

auto main()
    -> int
{
    string const a = ".\\This – by ABC.txt";    // Literal encoded as CP 1252.
    return rename( a.c_str(), "New.txt" ) == 0? EXIT_SUCCESS : EXIT_FAILURE;
}
[C:\my\forums\so\265] > dir /b *.txt File Not Found [C:\my\forums\so\265] > g++ r.cpp -fexec-charset=cp1252 [C:\my\forums\so\265] > type nul >"This – by ABC.txt" [C:\my\forums\so\265] > run a Exit code 0 [C:\my\forums\so\265] > dir /b *.txt New.txt [C:\my\forums\so\265] > _ [C:\my\forums\so\265] > wmic os get codeset /value | find "=" CodeSet=1252 [C:\my\forums\so\265] > _
// Source encoding: UTF-8
// Execution character set: irrelevant (everything's done in Unicode).
#include <stdlib.h>     // EXIT_SUCCESS, EXIT_FAILURE

#include <filesystem>   // In C++17 and later, or Visual C++ 2015 and later.
using namespace std::tr2::sys;

auto main()
    -> int
{
    path const old_path = L".\\This – by ABC.txt";    // Literal encoded as wide string.
    path const new_path = L"New.txt";
    try
    {
        rename( old_path, new_path );
        return EXIT_SUCCESS;
    }
    catch( ... )
    {}
    return EXIT_FAILURE;
}