使用opencv传中文文件崩溃
这个问题经过我的调试发现:
程序是在
while (*at && !isdigit(*at)) at++;
这个语句时crash的,但是跟进去是isdigit的问题,因为变量at的声明是char* at; 当at指向一个中文路径的时候,在传递给 isdigit时,*at类型很明显,隐式转换成为了int,因为 isdigit(int c)是这样声明的。所以这个时候一下子int c变成了一个负数:0xffffffb2,然后再往下跟进执行到 _ASSERTE((unsigned)(c + 1) <= 256);这个语句才崩溃的,因为强制转换成无符号的int,变成了一个很大的正数。明显不满足断言条件,所以程序当然crash了。
以上的crash情况只有在Debug版本才会crash,Release版本就不会出问题。原因已经在微软带的CRT库的源码中看出来了
结论:
应该是我们使用的问题,中文windows默认的locale是GB2312的,我们很可能却调用ASCII版本的 isdigit函数(所以遇到中文立即crash),这已经不符合逻辑了,咱不可能手动去改变locale。
下面是opencv源码中造成崩溃的函数源码(注意第三十行造成crash的语句):
1 static char* icvExtractPattern(const char *filename, unsigned *offset) 2 { 3 char *name = (char *)filename; 4 5 if( !filename ) 6 return 0; 7 8 // check whether this is a valid image sequence filename 9 char *at = strchr(name, '%'); 10 if(at) 11 { 12 int dummy; 13 if(sscanf(at + 1, "%ud", &dummy) != 1) 14 return 0; 15 name = strdup(filename); 16 } 17 else // no pattern filename was given - extract the pattern 18 { 19 at = name; 20 21 // ignore directory names 22 char *slash = strrchr(at, '/'); 23 if (slash) at = slash + 1; 24 25 #ifdef _WIN32 26 slash = strrchr(at, '\\'); 27 if (slash) at = slash + 1; 28 #endif 29 30 while (*at && !isdigit(*at)) at++; 31 32 if(!*at) 33 return 0; 34 35 sscanf(at, "%u", offset); 36 37 int size = (int)strlen(filename) + 20; 38 name = (char *)malloc(size); 39 strncpy(name, filename, at - filename); 40 name[at - filename] = 0; 41 42 strcat(name, "%0"); 43 44 int i; 45 char *extension; 46 for(i = 0, extension = at; isdigit(at[i]); i++, extension++) 47 ; 48 char places[10]; 49 sprintf(places, "%dd", i); 50 51 strcat(name, places); 52 strcat(name, extension); 53 } 54 55 return name; 56 }
下面是个最小的崩溃场景例子:
1 #include<ctype.h> 2 #include<stdio.h> 3 4 5 int main(){ 6 7 char *pPath = "测试视频"; 8 #if defined (_DEBUG) 9 if(!isdigit(*pPath)){ 10 printf("its not digit\n"); 11 } 12 #else 13 if(!iswdigit(*pPath)){ 14 printf("Release its not digit\n"); 15 } 16 #endif 17 return 0; 18 }
以下是一个测试:
1 #include <stdio.h> 2 #include <Windows.h> 3 4 int NarrowChar() { 5 char *pcAlpha = "啊"; 6 printf("pcAlpha = 0x%08X!\n", *pcAlpha); //0xFFFFFFB0 7 return 0; 8 } 9 10 int WideChar() { 11 //wchar_t *pwcAlpha = L"A"; 12 wchar_t *pwcAlpha = L"啊"; 13 printf("pwcAlpha = 0x%08X!\n", *pwcAlpha); //0x0000554A UTF-16-little-endian 14 //wprintf(L"pwcAlpha = 0x%08X!\n", *pwcAlpha); //0x0000554A 15 return 0; 16 } 17 18 int main(void) { 19 NarrowChar(); 20 WideChar(); 21 22 return 0; 23 }
由以上可以看出来,字符串这个前缀‘L’实质上是对字符串从ANSI字符集转换为Unicode字符集,也就是UTF-16编码。如果不加这个L前缀就是ANSI/ASCII字符集。
以上的问题我前博文已经研究过了。
################################update#############################
结论:
经过调试测试,其实就是cvCreateFileCapture函数返回失败的原因,在opencv 2.4.9和opencv2.4.10中就有问题,就是都返回失败,而2.4.6就不会
references:
http://www.cppblog.com/luonjtu/archive/2009/03/13/76332.html
http://comments.gmane.org/gmane.comp.lib.opencv.devel/1274
http://msdn.microsoft.com/en-us/library/windows/desktop/ff381407(v=vs.85).aspx
http://blog.csdn.net/xiaobai1593/article/details/7063535
http://stackoverflow.com/questions/8032080/how-to-convert-char-to-wchar-t