windows修复文件名乱码

从linux机器传中文文件名的文件到windows,会导致文件名乱码,这时因为linux编码是utf8,windows是gbk。找了一下好像没有类似linux的iconv,convmv的工具。于是自己手写了一个。已经开发成功并经过自测。取名叫utf8togbk.exe。主要功能是两个,一个是修复单个文件的乱码文件名,另一个是指定目录,递归修复包括此目录名在内和此目录里的所有乱码中文名。

贴代码,使用的vs2022,需要取消sdl检查,和至少支持c++17,因为用到了std::filesystem。

#include <windows.h>
#include <iostream>

using namespace std;

#include <filesystem>
namespace fs = std::filesystem;

string Utf8ToGbk(const char* src_str)
{
	int len = MultiByteToWideChar(CP_UTF8, 0, src_str, -1, NULL, 0);
	wchar_t* wszGBK = new wchar_t[len + 1];
	memset(wszGBK, 0, len * 2 + 2);
	MultiByteToWideChar(CP_UTF8, 0, src_str, -1, wszGBK, len);
	len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
	char* szGBK = new char[len + 1];
	memset(szGBK, 0, len + 1);
	WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
	string strTemp(szGBK);
	delete[] wszGBK;
	delete[] szGBK;
	return strTemp;
}
//————————————————
//版权声明:本文为CSDN博主「踏莎行hyx」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https ://blog.csdn.net/u012234115/article/details/83186386/

string GbkToUtf8(const char* src_str)
{
	int len = MultiByteToWideChar(CP_ACP, 0, src_str, -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len + 1];
	memset(wstr, 0, len + 1);
	MultiByteToWideChar(CP_ACP, 0, src_str, -1, wstr, len);
	len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len + 1];
	memset(str, 0, len + 1);
	WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
	string strTemp = str;
	if (wstr) delete[] wstr;
	if (str) delete[] str;
	return strTemp;
}
//————————————————
//版权声明:本文为CSDN博主「踏莎行hyx」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https ://blog.csdn.net/u012234115/article/details/83186386/

wstring s2ws(const std::string& s)
{
	size_t i;
	string curLocale = setlocale(LC_ALL, NULL);
	setlocale(LC_ALL, "chs");
	const char* _source = s.c_str();
	size_t _dsize = s.size() + 1;
	wchar_t* _dest = new wchar_t[_dsize];
	wmemset(_dest, 0x0, _dsize);
	//mbstowcs_s(&i, _dest, _dsize, _source, _dsize);
	mbstowcs(_dest, _source, _dsize);
	wstring result = _dest;
	delete[] _dest;
	setlocale(LC_ALL, curLocale.c_str());
	return result;
}

string getName(string path)
{
	int pos = path.find_last_of("\\");
	if (pos != -1)
	{
		string ret = path.substr(pos + 1);
		return ret;
	}
	pos = path.find_last_of("/");
	string ret = path.substr(pos + 1);
	return ret;
}

void xiufuluanma(string dir)
{
	for (auto& i : fs::directory_iterator(dir))
	{
		string s = i.path().string(); 
		fs::path sourceFilePath(s);
		string destinationPath = dir + Utf8ToGbk(getName(s).c_str());

		try
		{
			fs::path destinationFilePath(destinationPath);
			fs::rename(sourceFilePath, destinationFilePath);
		}
		catch (const fs::filesystem_error& e)
		{
			std::cerr << "文件名修复失败:" << e.what() << std::endl;
			ExitProcess(0);
		}
	}

	for (auto& i : fs::directory_iterator(dir))
	{
		string s = i.path().string();
		wstring ws = s2ws(s);

		int ret = GetFileAttributes(ws.c_str());
		if (ret == FILE_ATTRIBUTE_DIRECTORY)
		{
			if (s.back() != '\\' || s.back() != '/')
			{
				s += '\\';
			}
			xiufuluanma(s);
		}
	}
}

char siweilicheng[];

int main(int argc, char* argv[])
{
	if (argc < 2)
	{
		printf("usage \r\n\
utf8togbk.exe utf8乱码文件名\r\n\
utf8togbk.exe utf8乱码文件所在目录\n\r\
utf8togbk.exe -s utf8乱码字符串\n\r\
utf8togbk.exe --info");
		return 0;
	}

	int ret = strcmp(argv[1], "-s");
	if (ret == 0 && argc == 3)
	{
		string s = Utf8ToGbk(argv[2]);
		cout << s << endl;
		return 0;
	}

	ret = strcmp(argv[1], "--info");
	if (ret == 0 && argc == 2)
	{
		cout << siweilicheng << endl;
		return 0;
	}

	if (argc != 2)
	{
		printf("usage \r\n\
utf8togbk.exe utf8乱码文件名\r\n\
utf8togbk.exe utf8乱码文件所在目录\n\r\
utf8togbk.exe -s utf8乱码字符串\n\r\
utf8togbk.exe --info");
		return 0;
	}

	bool isfile = false;
	string file = argv[1];
	wstring wfile = s2ws(file);

	ret = GetFileAttributes(wfile.c_str());
	if (ret == -1)
	{
		return 0;
	}

	if (ret == FILE_ATTRIBUTE_DIRECTORY)
	{
		isfile = false;
	}
	else
	{
		isfile = true;
	}

	try
	{
		fs::path sourceFilePath(file);
		string destinationPath = Utf8ToGbk(file.c_str());
		fs::path destinationFilePath(destinationPath);
		fs::rename(sourceFilePath, destinationFilePath);
		file = destinationPath;
	}
	catch (const fs::filesystem_error& e)
	{
		std::cerr << "文件名修复失败:" << e.what() << std::endl;
	}

	if (!isfile)
	{
		if (file.back() != '\\' || file.back() != '/')
		{
			file += '\\';
		}
		xiufuluanma(file);
	}

	return 0;
}

char siweilicheng[] = { "从linux传文件到windows,因为编码不一致的问题,导致在windows查看中文文件名乱码。于是写了一个工具,能将utf8乱码中文字符串转为gbk正常的中文字符串。\n\r\
但是仅仅如此还不够好用。实际上往往还需要两个需求,一个是直接修复乱码的文件名,另一个是指定目录,批量修复里面所有的乱码的文件名。\n\r\
文件内容基本不会乱码,因为linux和windows文件内容现在都支持utf8,暂时不管。\n\r\
现在的难点是文件名与普通字符串的区分,对于这个,加一个参数 - s表示修复字符串,不加表示修复文件名。\n\r\
另一个难点是,如何识别文件名是否乱码,否则会导致正常的不乱码的文件名变为乱码。\n\r\
其它的额外功能暂不考虑。\n\r\
\n\r\
开发的过程中发现需要判断参数是文件还是目录。准备通过反斜杠`\\`或者正斜杠`/`来判断,如果存在斜杠,就是目录,如果不存在斜杠,就是文件名。\n\r\
这样判断是不行的,因为如果文件名写绝对路径或相对路径时,也是会带有斜杠的。\n\r\
所以只能通过代码获取文件信息来判断。\n\r\
先判断文件是否存在,不存在结束。如果存在,就判断是文件名还是目录。\n\r\
发现GetFileAttributes的返回值就能直接判断了, - 1表示不存在,16表示目录,其它表示文件。\n\r\
\n\r\
如何区别文件名是否乱码还是比较有难度的。因为要识别编码是gbk还是utf8,这项工作的难度已经大于本程序了。\n\r\
通常情况下,gdk编码比utf8小,因为gbk只支持中文,utf8支持全世界文字。\n\r\
所以可以通过比较大小来简单判断,如果转换后编码体积变小,就转换,如果转换后体积没有变小,则不转换。以免正常的gbk编码被转成乱码,导致无法恢复。\n\r\
这样子还是会有问题,因为utf8转为gbk后,如果继续把gbk编码当作utf8转为gbk,体积还是有可能变小,还是能继续转为乱码。\n\r\
所以暂时要对用户提要求了,要求操作的文件夹里面必须都是中文乱码的,否则后果自负。\n\r\
其实正常情况下,从linux传文件到windows,中文是一定100 % 乱码的,英文是不会乱码的,而转换的过程中,也只是将中文乱码转为中文,英文无论是gbk,还是utf8,怎么互转都不会乱码。\n\r\
这样需求已实现,可是我还是想再实现一个递归操作。该目录下所有子目录的文件都修复。\n\r\
递归想起来难,实现起来还是比较容易的。\n\r\
测试递归的过程中发现问题,需要把目录名先修复,否则后面是按修复后的目录来重命名,这样会导致找不到目录而修改失败。\n\r\
先修复目录名也会有问题,因为目录名修复后,后面再次修复就变成乱码了。\n\r\
可是为什么第一次测试里面的目录不会乱码呢?\n\r\
不知道为什么第一次成功了,现在想重现都不容易重现。\n\r\
正确的做法应该是计算偏移,记录修改后的目录名称,不能再次修改目录名。\n\r\
成功了。\n\r\
如果还有bug靠用户了。\n\r\
!!!这个不要随意乱用,否则可能会将系统造成大麻烦。因为递归操作可以大量将正常的中文改为乱码。\n\r\
考虑到安全性可能比功能更重要,所以准备修改为,如果万一将一个正常的中文文件名改为乱码文件名,并且乱码导致更名失败一次,就结束进程。\n\r\
开发程序时的思维历程和最后输出的源代码同样重要,所以我将思维历程和源代码放一起,并编译到可执行文件里。 \n\r\
最后直接拿可执行文件测试时发现问题,递归修改目录时,里面的文件会跑出来。\n\r\
原因是目录结尾缺少`\\`或`/`,导致目录直接和里面的文件名连在一起出错。加一个判断改善就好了。\n\r\
而且现在windows能同时支持目录里的`\\`或`/`,还是比较方便的。\n\r\
"};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值