让fopen打开在不同的代码页下创建的文件
来源:互联网 发布:40万的suv 知乎2017 编辑:程序博客网 时间:2024/05/23 02:15
让fopen打开在不同的代码页下创建的文件
分析: 中文Win2000的代码页是936,在这个代码页下的字符的ANSI编码和UNICODE编码是一一对应的。 在文件迁移到英文Win2000后,问题就出现了。 英文Win2000的代码页是437。一个中文字符的UNICODE映射到437是没有任何意义的,这称为unmappable character。一般情况下,unmappable character的转换结果会是一个'?'。所以转换之后,文件名中原有的信息就丢失了。这正是英文Win2000下fopen不能打开中文文件的原因。 解决: 前面提到了,Win2000的文件系统是用ANSI编码来保存短文件名的。我们可以利用它来解决问题。 怎么得到短文件名呢?有一个API是做这事的: DWORD GetShortPathName(LPCTSTR lpszLongPath, LPTSTR lpszShortPath, DWORD cchBuffer ); GetShortPathName()需要一个长文件名(其实是PATH)作为参数,这就意味着我们只能使用GetShortPathNameW(),那么我们得到的短文件名也是UNICODE。那这不是存在和长文件名一样的问题吗??? 注意到一个事实:一个ANSI字符串,在不同的代码页有不同的含义。但是,在任意一个代码页下,将ANSI转为UNICODE,然后再从UNICODE转回ANSI,这个字符串保持不变,不会丢失任何信息!!! 示例代码: FILE * file = NULL; wchar_t wsShortName[1000] = {0,}; OPENFILENAMEW ofn; // Initialize OPENFILENAME fclose(file);
问题:
英文Win2000下,应用程序操作一个文件名中带中文字符的文件时出错。
文件是在中文Win2000下创建好之后,再拷贝到英文Win2000的,初步认为这涉及到代码页的问题!
应用程序使用了一个第三方的工具包,提供的接口需要一个ANSI编码的文件名,而不是UNICODE的。
进一步的分析发现,工具包是用fopen()来打开文件的。
于是,问题变成了如何让fopen在不同的代码页之间也能正常工作?
要解决问题,必须先弄清楚问题产生的具体原因。
查看文档,Win2000的文件系统在保存文件时,实际记录了文件的两个名字:长文件名和短文件名。而长文件名是按UNICODE保存的,短文件名则是按ANSI保存的!
显然上面的操作都是基于长文件名的。
那么问题可以这样来理解:
文件的长文件名是UNICODE,而fopen()需要ANSI作为文件名参数,所以如果你想使用fopen(),必然需要UNICODE和ANSI之间的转换,而这个转换可能是你来做也可能是系统来做。比如你使用GetOpenFileNameA()来得到文件名,那么转换就不用你关心了;如果你非要用GetOpenFileNameW()的话,那么你的代码的下一行可能就是WideCharToMultiByte()了。总之,fopen的使用不会有什么问题。
所以,我们可以放心地把得到的短文件名用WideCharToMultiByte()转为ANSI,再交给fopen使用。
wchar_t szFile[MAX_PATH];
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '/0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = L"All/0*.*/0Text/0*.TXT/0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileNameW(&ofn))
{
GetShortPathNameW(ofn.lpstrFile, wsShortName, 1000);
char sShortName[1000] = {0};
int length =WideCharToMultiByte(CP_ACP, 0, wsShortName, -1, sShortName, 1000, NULL, NULL);
file = fopen(sShortName, "rb");
}
分析:
文件是在中文Win2000下创建好之后,再拷贝到英文Win2000的,初步认为这涉及到代码页的问题!
应用程序使用了一个第三方的工具包,提供的接口需要一个ANSI编码的文件名,而不是UNICODE的。
进一步的分析发现,工具包是用fopen()来打开文件的。
于是,问题变成了如何让fopen在不同的代码页之间也能正常工作?
要解决问题,必须先弄清楚问题产生的具体原因。
查看文档,Win2000的文件系统在保存文件时,实际记录了文件的两个名字:长文件名和短文件名。而长文件名是按UNICODE保存的,短文件名则是按ANSI保存的!
显然上面的操作都是基于长文件名的。
那么问题可以这样来理解:
中文Win2000的代码页是936,在这个代码页下的字符的ANSI编码和UNICODE编码是一一对应的。
文件的长文件名是UNICODE,而fopen()需要ANSI作为文件名参数,所以如果你想使用fopen(),必然需要UNICODE和ANSI之间的转换,而这个转换可能是你来做也可能是系统来做。比如你使用GetOpenFileNameA()来得到文件名,那么转换就不用你关心了;如果你非要用GetOpenFileNameW()的话,那么你的代码的下一行可能就是WideCharToMultiByte()了。总之,fopen的使用不会有什么问题。
在文件迁移到英文Win2000后,问题就出现了。
英文Win2000的代码页是437。一个中文字符的UNICODE映射到437是没有任何意义的,这称为unmappable character。一般情况下,unmappable character的转换结果会是一个'?'。所以转换之后,文件名中原有的信息就丢失了。这正是英文Win2000下fopen不能打开中文文件的原因。
解决:
前面提到了,Win2000的文件系统是用ANSI编码来保存短文件名的。我们可以利用它来解决问题。
怎么得到短文件名呢?有一个API是做这事的:
DWORD GetShortPathName(LPCTSTR lpszLongPath, LPTSTR lpszShortPath, DWORD cchBuffer );
GetShortPathName()需要一个长文件名(其实是PATH)作为参数,这就意味着我们只能使用GetShortPathNameW(),那么我们得到的短文件名也是UNICODE。那这不是存在和长文件名一样的问题吗???
注意到一个事实:一个ANSI字符串,在不同的代码页有不同的含义。但是,在任意一个代码页下,将ANSI转为UNICODE,然后再从UNICODE转回ANSI,这个字符串保持不变,不会丢失任何信息!!!
所以,我们可以放心地把得到的短文件名用WideCharToMultiByte()转为ANSI,再交给fopen使用。
示例代码:
FILE * file = NULL;
wchar_t wsShortName[1000] = {0,};
OPENFILENAMEW ofn;
wchar_t szFile[MAX_PATH];
// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '/0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = L"All/0*.*/0Text/0*.TXT/0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileNameW(&ofn))
{
GetShortPathNameW(ofn.lpstrFile, wsShortName, 1000);
char sShortName[1000] = {0};
int length =WideCharToMultiByte(CP_ACP, 0, wsShortName, -1, sShortName, 1000, NULL, NULL);
file = fopen(sShortName, "rb");
}
fclose(file);
- 让fopen打开在不同的代码页下创建的文件
- 文件的打开 (fopen()函数)
- 文件的打开(fopen函数)
- 文件的打开(fopen函数)
- 文件的打开(fopen函数)
- 文件的具体操作,fopen在不同场景的参数设置
- Qt、Vc下用fopen打开中文名字的文件
- 打开文件 fopen, open 在某些时候的区别 linux
- fopen打开文件的方式的问题
- fopen函数以‘rb’模式 和 ‘r’ 模式打开文件的不同
- fopen打开相对路径的文件
- fopen打开文件的模式详解
- fopen打开相对路径的文件
- 留心一下fopen打开文件的模式
- php 中关于 fopen 如何打开或创建中文文件的使用说明
- fopen() 的打开模式
- 常用的编译宏定义:可以让代码在不同的编译情况下执行
- fopen创建指定大小的文件
- 尝试
- PlugIn
- [收集]c#.net函数和方法集
- 在 C# 中实现 Singleton
- 使代码简洁的五条忠告--Delphi园地
- 让fopen打开在不同的代码页下创建的文件
- DataGrid Web Control 基本操作
- 优化.NET异常处理
- How to Bind an ArrayList to a DataGrid
- j2me实现图像旋转
- UNIX下实现终端打印的几种方法
- 在C#中用最简洁有效的代码执行存储过程并返回数据
- C#执行存储过程的简化 选择自 spanzhang 的 Blog
- ASP中如何执行存储过程? 选择自 leonduan 的 Blog