fstream中文路径

fstream中文路径在C++的标准库中,std::fstream是个挺好用的文件读写流,操作文件很方便,因为是C++标准库,所以没有其它的环境依赖。在使用fstream过程中,有个打开中文路径文件会失败的问题,自己的代码中一直没处理好,这几天终于有点闲心,把这里改透。涉及很多知识点,也是个遗留已久的问题,特此做个记录。在最后用了个一劳永逸的解决此问题方法:将fstream、FILE再包装下。中文路径使用fstream调试程序过程中,发现打开含中文路径的文件时,会打开失败。查了一些资料,说在VS2008、vs200..

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

目录

中文路径

编译器

统一字符集

修正方法

改全局的Locale

使用wstring

字符集改为宽字符集

升级编译器

一劳永逸

将fstream再包装

总结


在C++的标准库中,std::fstream是个挺好用的文件读写流,操作文件很方便,因为是C++标准库,所以没有其它的环境依赖。在使用fstream过程中,有个打开中文路径文件会失败的问题,自己的代码中一直没处理好,这几天终于有点闲心,把这里改透。涉及很多知识点,也是个遗留已久的问题,特此做个记录。

在最后用了个一劳永逸的解决此问题方法:将fstream、FILE再包装下。

1.中文路径

使用fstream调试程序过程中,发现打开含中文路径的文件时,会打开失败。查了一些资料,说在VS2008、vs2005中,fstream的open函数中,会判断当前的全局编码环境,导致含中文的路径打开失败,也提到了很多修改方法,如让修改全局的Locale改为宽字符、使用宽字符集wstring。

我当时的修改方法是调用重载的open(wstring),即将string的路径,转为wstring,然后fstream就可以打开有中文路径文件了,缺点是在string转wstring时需要调用windows的函数,产生了一个外部依赖,且多了几行代码。

编译器

我所使用的开发环境是vs2008,有些文章说只在vs2005、vs2008的环境中才有这个问题,好在我的开发环境中有vs2015,所以在vs2015中也测试下。很容易执行这个测试,vs2015中执行新建win32控制台程序,在main函数中添加测试代码,定义fstream打开文件,重点是文件路径要有中文。

测试代码如下:

#include <fstream>

int main()

{

    std::fstream st;

    st.open(“D:/temp/fstream测试/测试1.log”, std::fstream::app); //含中文的

    printf(“%s\r\n”, (st.is_open() ? “sucess” : “failed”));

    system(“pause”);

    return 0;

}

测试执行比对后,确认在vs2008中不能打开中文路径文件,而在2015中可以打开中文路径文件,显然这个问题和编译器是有关系的。我想linux环境中会怎么样,还需要测试下Gcc的现象、版本支持情况。在C++标准库中,不约定std::fstream的字符集支持与否的,这个问题肯定和编译器有关。

测试项目的属性界面中,字符集都是默认设置的“使用Unicode字符集”,对比也是有效的。

统一字符集

程序运行的操作系统环境有字符集、程序源码文件有字符集、程序运行后处理字符也有字符集,改字符集是个繁琐的事情,源码文件、软件环境、都需要改。

避免字符集的问题,最简单就是统一字符集,各个涉及字符集的地方都统一,我们写的程序就适应环境的字符集,此时问题就出现了,对于跨平台的开发,linux和windows差别太大了,会引入很多问题。

2.修正方法

为了在vs2008中使用std::fstream,可供使用的方法也有很多,最后总结罗列下面几种改进方法。

改全局的Locale

改全局的Locale,在调用open前调用setLocale修改为多字符集,open之后再改回来。产品是个多线程的程序,在基础库中修改全局的环境,可能会导致访问异常、崩溃,这不是个好方法。

然而这个只需要一行代码即可,修改最简单。复杂软件系统中不要随意改全局编码,不要这么用。

setlocale(LC_ALL,”Chinese-simplified”);

std::fstream stream;

stream.open(“d:/数据/a.log”);

使用wstring

在fstream的接口方法中,重载有多个open函数,包括传入string、wstring等,实际测试确认调用重载传入wstring的open函数时,可以打开中文路径。

定义一个string转wstring的函数,使用fstream的open函数时,先调用转换为wstring,然后调用open函数。这样引入了一个多字符转宽字符的函数,多几行调用,整体的稳定、可靠是没问题的。

关于string转wstring,不要用C标准库的mbstowcs,该方法不支持中文,调用mbstowcs转换的中文会编程乱码。

#include <Windows.h>

//将string转换成wstring  

inline wstring string2wstring(string str)  

{  

wstring result;  

//获取缓冲区大小,并申请空间,缓冲区大小按字符计算  

int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);  

TCHAR* buffer = new TCHAR[len + 1];  

//多字节编码转换成宽字节编码  

MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);  

buffer[len] = ‘\0’;             //添加字符串结尾  

//删除缓冲区并返回值  

result.append(buffer);  

delete[] buffer;  

return result;  

}  

字符集改为宽字符集

根源上还是字符集的问题,所有的string都改为wstring。我的软件中,一部分代码在windows vs2008开发、另一部分用的QtCreator开发、目前运行在windows,导致源码文件的默认编码是gb2312,软件运行的默认Locale也是多字符集。

由于有跨平台的考虑,要减少环境的依赖、尽量只调用标准库、少改动代码,所以所有的编码应统一utf8,工程量太大,不太现实,后续再改进。

升级编译器

最简单的方法是升级编译器,不要用vs2008。升级编译器,不是小事,看使用者的习惯,代码会不会有影响。升级编译器、编译环境,还是有些繁琐的。导致产品代码,要对编译器有要求,约定了最低编译器版本。

3.一劳永逸

几种方法中都有缺点,转wstring的方式引入了操作系统的依赖,在我的产品代码中想尽量少依赖,可以用升级编译器的方式,但有几个库是vs2008的,都升级有些繁琐。

想到个简单的、一劳永逸的方法,复用FILE指针、fopen函数可以支持中文路径、各种版本编译器中表现也一致。

将fstream再包装

在fstream的构造函数中,有输入FILE指针,在fstream构造函数传入FILE,所以可以先用fopen函数打开带中文路径的文件,然后构造fstream,之后就正常使用fstream即可。

基于再包装wrap的方法,建立WrapFstream类,封装这几个操作。

  1. 成员变量包括std::fstream、FILE,对外接口主要open函数,在open函数内部用fopen打开FILE指针,然后构造fstream对象,此时不会有中文路径的问题。

bool open(const std::string & fileName, const char * mode){

close();

file = fopen(fileName.c_str(), mode);

if (file != NULL)

{

st = new std::fstream(file);

return st->is_open();

}

return false;

}

  1. 在关闭函数close中,需要释放资源。

void close(){

if (st != NULL){

if (st->is_open())

st->close();

delete st;

st = NULL;

}

if (file != NULL)

fclose(file);

file = NULL;

st = NULL;

}

  1. 然后是对外提供fstream接口方法,直接将fstream返回给调用者,调用者直接使用。
  2. 完整代码:

/**

 * 将fstream再包装

 * 单元测试在CoreTest/test_TSore.cpp

 */

class WrapFstream

{

std::fstream * st;

FILE * file;

public:

MFileStream(){

file = NULL;

st = NULL;

}

~MFileStream(){

close();

}

bool open(const char * fileName, const char * mode){

return this->open(string(fileName), mode);

}

bool open(const std::string & fileName, const char * mode){

       ……………….

}

bool is_open(){

if (st != NULL)

return st->is_open();

return false;

}

void close(){

……….

}

std::fstream * stream(){

return st;

}

std::fstream & obj(){

return (*st);

}

};

总结

vs2008中fstream用string不能打开中文路径,要用wstring.

该问题设计多个知识点。文件编码等,有几个方式不能有效解决

1.该全局的编码setLocate(‘c’). 该方法不可,由于TStore是基础库,整个软件是多线程,所有这个setLocate可能导致多线程的访问异常、崩溃。

2.调用C库函数转换为宽字符串mbstowcs. 该方法不可,不能转换中文,VC的实现中只是在每个字节前插入一个x0。

3.调用系统函数windows的多字符串转宽字符MuilteToWideChar,这个是可行的. 这几年一直这么跑,是windows环境,跨平台需要改下。

4.有个文章说vc2005、vc2008有这个问题,而vc2005之前没有,作者跟着fstream的open函数代码,其中有判断全局环境编码的如果是’C’那么将string.c_str()直接转了(wchar*),然后调用CreateFile,所有不能打开。

5.在vc2015确认无此问题。

6.现在用FILE的fopen先打开,然后传递给fstream,再封装下。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/216555.html原文链接:https://javaforall.net

(0)
上一篇 2025年7月11日 下午1:15
下一篇 2025年7月11日 下午1:43


相关推荐

  • 字符串矩阵 matlab(matlab 字符串矩阵)

    这里字符串矩阵(或者有更标准的名称?)是指一个矩阵中的每个元素为changdua

    2022年4月16日
    61
  • 为你的SublimeText编辑器安装变量命名插件神器codelf

    为你的SublimeText编辑器安装变量命名插件神器codelf身为编程小白,是否有为变量命名而困扰呢,PhilKarlton曾说“在CS领域中,有两件事是非常难的,一个是缓存失效,一个是命名。”因此,有一款能为你的变量自动提供命名的工具是不是特别关键呢?本文仅仅简单介绍了SublimeText手动安装包的方法。此外,附上最新版的SublimeText4安装教程。

    2022年5月23日
    66
  • Object转成JSONObject

    Object转成JSONObjectpublicvoidonNext(Objecto){LogUtil.i(“getFavorites”,”json=”+o.toString());//JSONObjectjson=JSON.parseObject(“{\”code\”:1001,\”timestamp\”:\”2018-11-0503:40:54\”}”);…

    2022年6月14日
    47
  • 进程同步和线程同步

    进程同步和线程同步怎样同步多个线程或多个进程的活动。为允许在线程或进程间共享数据,同步是必需的。互斥锁和条件变量是同步的基本组成部分。互斥锁和条件变量出自POSIX.1线程标准,它们总是可用来同步一个进程内的各个线程的。如果一个互斥锁或条件变量存放在多个进程间共享的某个内存中,那么POSIX还允许它用于这些进程间的同步。互斥锁、条件变量、读写锁、信号量均可用于进程、线程的同步。多线程同步方法1)互斥锁互斥锁是最基

    2022年7月15日
    17
  • 使用Python打包含有pymssql成exe所躺的坑「建议收藏」

    使用Python打包含有pymssql成exe所躺的坑「建议收藏」一、如何打包Python打包exe文件简单运用pyinstaller库就行了1)安装pyinstaller库(自行安装)2)win+R打开运行窗口输入“powershell”3)输入pyinstaller-F路径\文件名.py(打包py文件的路径,py不能省略)看到successfully即为打包成功,但不一定能运用的!!!(划重点,下面便是我躺过的坑)二、打包exe成功但运行遇报错(打包成功,但双击运行一闪而过):打包的文件代码在这里想查清报错win+R打开运行窗口输入“cmd”

    2022年6月15日
    38
  • linux安装anaconda及配置pytorch环境

    文章目录下载anaconda使用conda创建新环境判断是否安装成功判断pytorch是否安装成功检验是否可以使用GPU下载anaconda这里有多种方法,可以下载到windows然后通过xftp传到linux服务器上,也可以使用wget命令直接在linux上下载我这里使用的是第二种方法,下载源使用的是官网的官网,如果觉得速度太慢可以使用清华镜像源,选择自己需要的版本就行wgethttps://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_

    2022年4月14日
    171

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号