当我使用opencv的API cvLoadImage(const char *filename, int iscolor)
时,它接受const char *
作为文件名。当文件名不是ASCII字符时,我尝试将其转换为UTF8字符串。它失败了,因为在cvLoadImage()
中调用的fopen()
无法将文件名的字符从字面上解释为ASCII字符串。如果试图打开文件名,我可能会使用_wfopen()
,但如果在第三方库中调用fopen()
,有什么方法可以处理这个问题吗?非常感谢。
使用GetShortPathName。它将返回文件的旧名称(8.3),您应该能够将其转换为char*
,因为它不应该包含任何非ASCII字符。
我刚刚用一些特定语言的字符测试了它,它就像我描述的那样工作。我已经使用fopen成功地从C:łęłęłąóąóą.tsttgbb
打开了一个文件。
setlocale(LC_ALL, ".65001");
fopen(u8"中文路径.txt", "rb"); //window7(中文) vs2017 ok
快速搜索一无所获,但人们表示无法完成。如果你不能改变cvLoadImage
(这是合理的,你不想搞砸它),你可以试着欺骗它
- 您可以使用
CreateSymbolicLink
创建指向该文件的链接。不过,我不确定它是否能工作,因为MKLINK
命令行实用程序需要管理权限 - 如果无法创建符号链接,则可以始终使用仅ASCII名称将文件复制到其他位置
- 如果你真的不想复制文件,并且符号链接不起作用,你可以创建一个文件代理-创建一个仅具有ASCII名称的命名管道,并将从管道读取的每个数据转换为从文件读取
不过,我会选择选项1或2——简单得多。
这是对这个问题的最新贡献。我查看了运行库的来源(微软很乐意提供),发现我可以用以下代码替换fopen用来映射ANSI字符串的例程(只需将其链接到你的exe中,它就会替换运行库中的例程)。
列出的版本适用于使用v141_xp工具包的Visual Studio 2017。我还没有在其他版本中测试过它,但我想可能需要一些小的更改(比如例程本身的名称)。如果有问题的库是一个DLL,它当然不会工作。随心所欲。
#ifdef _DEBUG
#define _NORMAL_BLOCK 1
#define _CRT_BLOCK 2
#define _malloc_crt(s) (_malloc_dbg (s, _CRT_BLOCK, __FILE__, __LINE__))
#else
#define _malloc_crt _malloc_base
#endif
// A hack to make fopen et al accept UTF8 strings (as at Visual Studio 2017), see:
// D:Program Files (x86)Windows Kits10Source10.0.10240.0ucrtinternalstring_utilities.cpp
// D:Program Files (x86)Windows Kits10Source10.0.10240.0ucrtinccorecrt_internal_traits.h
extern "C" BOOL __cdecl __acrt_copy_path_to_wide_string (char const* const path, wchar_t** const result)
{
#if _MSC_VER != 1910
#define STRINGIZE_HELPER(x) #x
#define STRINGIZE(x) STRINGIZE_HELPER(x)
__pragma (message (__FILE__ "(" STRINGIZE (__LINE__) ") : Error: Code not tested for this version of Visual Studio"));
#endif
assert (path);
assert (result);
// Compute the required size of the wide character buffer:
int length = MultiByteToWideChar (CP_UTF8, 0, path, -1, nullptr, 0);
assert (length > 0);
*result = (wchar_t *) _malloc_crt (T2B (length));
// Do the conversion:
length = MultiByteToWideChar (CP_UTF8, 0, path, -1, *result, length);
assert (length);
return TRUE;
}