vs2015 mfc中用IXMLDOMDocumentPtr 读取带有汉字的xml文件

之前用IXMLDOMDocumentPtr写了个小工具,读取xml文件后,对它进行处理,比如修改项、删除项啥的,然后保存。

今天有人反馈说有 bug,有的文件没有修改。拿来一看,确实没有修改,跟踪发现,load()文件失败。代码流程如下:

int CheckXmlFile(CString strFileName)
{
  IXMLDOMDocumentPtr xmlDoc;
  HRESULT hr = xmlDoc.CreateInstance(__uuidof(DOMDocument60));
  VARIANT_BOOL bSuccess = FALSE;
  hr = xmlDoc->load(CComVariant(strFileName), &bSuccess);
  if (FAILED(hr) || !bSuccess)
      return -3;

  ...  // 处理代码
  return 0;
}

工程采用UNICDOE,所以strFileName是unicode字符串

发现这里返回了3,load()失败。然后用notepad++打开这个xml文件,一看里面有个值带有汉字,晕。然后在notepad++上看编码,一看是  ANSI 编码,然后通过编码菜单“转为 UTF-8 编码”,然后保存。再来查看程序,这次发现load()成功了。

这样也麻烦,从xml文件的来源上,没法控制它的原始编码,看来还得自己处理。

 我的处理办法就是如果这里load()失败,就自己读取文件然后转码,再进行loadXML():

int CheckXmlFile(CString strFileName)
{
  IXMLDOMDocumentPtr xmlDoc;
  HRESULT hr = xmlDoc.CreateInstance(__uuidof(DOMDocument60));
  VARIANT_BOOL bSuccess = FALSE;
  hr = xmlDoc->load(CComVariant(strFileName), &bSuccess);
  if (FAILED(hr) || !bSuccess)
  {
      // 参考 https://blog.csdn.net/zeuskaaba/article/details/4082826
      VARIANT vtName;
      vtName.vt = VT_BSTR;
      CString fileContString;
      int fileLen = gReadTextFile(strFileName, fileContString);
      if (fileLen < 7)
        return -3;    // 由于我这里xml文件肯定有一些内容,所以如果内容过短,我就直接返回不处理了
      vtName.bstrVal = fileContString.AllocSysString();
      hr = xmlDoc->loadXML(vtName.bstrVal, &bSuccess);
      SysFreeString(vtName.bstrVal);
      if (FAILED(hr) || !bSuccess)
         return -3;
  }

  ...  // 处理代码
  return 0;
}

这里关键的就是gReadTextFile()函数了,因为我的工程中还需要读取其它的文本文件,所以我就写了这个通用函数。另外我的文本文件都不会过长,所以就直接用一个CString把内容返回出来。

这个gReadTextFile()中,先读取文件,然后判断文件编码类型,然后再确定如何转成unicode编码,其中参考了很多其它的网址,下面直接上代码:

CString gStr2U(const CStringA str, CString& strOut, bool useAnsiPage /*= false*/)
{
	USES_CONVERSION;
	if (useAnsiPage)
		_acp = CP_ACP;	// 使用默认的线程值,会在有的电脑上失败,即转换出来的内容完全不对
	strOut = A2W(str);
	return strOut;
}

/*
https://www.cnblogs.com/AkazaAkari/p/8686327.html
*/
#define CHECK_LENGTH 1024000
int is_utf8_string(char *utf)
{
	int length = strlen(utf);
	int check_sub = 0;
	int i = 0;

	if (length > CHECK_LENGTH)  //只取前面特定长度的字符来验证即可  
	{
		length = CHECK_LENGTH;
	}

	for (; i < length; i++)
	{
		if (check_sub == 0)
		{
			if ((utf[i] >> 7) == 0)         //0xxx xxxx  
			{
				continue;
			}
			else if ((utf[i] & 0xE0) == 0xC0) //110x xxxx  
			{
				check_sub = 1;
			}
			else if ((utf[i] & 0xF0) == 0xE0) //1110 xxxx  
			{
				check_sub = 2;
			}
			else if ((utf[i] & 0xF8) == 0xF0) //1111 0xxx  
			{
				check_sub = 3;
			}
			else if ((utf[i] & 0xFC) == 0xF8) //1111 10xx  
			{
				check_sub = 4;
			}
			else if ((utf[i] & 0xFE) == 0xFC) //1111 110x  
			{
				check_sub = 5;
			}
			else
			{
				return 0;
			}
		}
		else
		{
			if ((utf[i] & 0xC0) != 0x80)
			{
				return 0;
			}
			check_sub--;
		}
	}
	return 1;
}


/*
作者:游学四方
来源:CSDN
原文:https://blog.csdn.net/blackbattery/article/details/78244178
版权声明:本文为博主原创文章,转载请附上博文链接!
//*/
CString g_strUString;
CString gUTF82WCS(const char* szU8)
{
	//预转换,得到所需空间的大小;
	int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);

	//分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间
	wchar_t* wszString = new wchar_t[wcsLen + 1];

	//转换
	::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);

	//最后加上'\0'
	wszString[wcsLen] = '\0';

	g_strUString = wszString;
	/*
	CString unicodeString(wszString);

	delete[] wszString;
	wszString = NULL;

	return unicodeString;
	/*/
	return g_strUString;
	//*/
}

/*
读取一个文本文件
返回值:
0 - 打开失败,或者文件为空
>0 :成功读取的文件长度,单位:字节
*/
int gReadTextFile(CString strFileName, CString& fileContString)
{
	FILE* filePtr = _tfopen(strFileName.GetString(), TEXT("r+b"));
	strFileName.ReleaseBuffer();
	if (filePtr == NULL)
		return 0;

	fseek(filePtr, 0, SEEK_END);
	int fileLen = ftell(filePtr) + 16;
	fseek(filePtr, 0, SEEK_SET);
	char* fileContPtr = new char[fileLen];
	memset(fileContPtr, 0, fileLen);
	fread(fileContPtr, 1, fileLen, filePtr);
	fclose(filePtr);
	filePtr = NULL;

	int retv = strlen(fileContPtr);
	if (is_utf8_string(fileContPtr))
		fileContString = gUTF82WCS(fileContPtr);
	else
		gStr2U(fileContPtr, fileContString, true);
	delete[] fileContPtr;
	fileContPtr = NULL;
	return retv;
}

其中is_utf8_string()函数,其判断的长度可以根据实际的项目进行修改,对我的项目来说,这个需要判断长度一般有个1KB就完全足够了,但这里为了保险,我改到了最长将近1MB。

思路就是选按照ANSI编码读取,然后判断编码类型是否是utf-8,如果是,就用这个方法来转,否则就用普通的方法来转。这里只判断了两种情形,估计不能应付所有情况,看我另外一个工程中的判断,多了一个分支:

int fenc;
memcpy(&fenc, fileContPtr, 2);
if (fenc != 0xfeff)
{
    if (is_utf8_string(fileContPtr))
		fileContString = gUTF82WCS(fileContPtr);
	else
		gStr2U(fileContPtr, fileContString, true);
}
else
{
    // 直接就是unicode了,所以就不用再转换了
    fileContString = (TCHAR*)fileContPtr;
}

最后的这个转换,差不多可以应付绝大多数的编码转换了,自从我改成这样的判断之后,那个工程就没有再反馈过问题了。

这么折腾一下,loadXML()带汉字的内容也是成功的了

这里的主要问题就是字符的编码,确实有点麻烦,我都快被绕晕了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值