Unicode以及字符集转换

曾经碰到一个问题,
项目需要支持日文操作系统,但是没有编译成unicode程序。
然后在一个解析用户输入路径的地方出问题了。
原因是日文的"表"这个汉字,日文编码格式下,低字节和反斜杠'/'编码一样,解析的时候把它当成路径的分隔符了。:-(
// 項: 8D 80 (Shift_JIS)
// 目: 96 DA (Shift_JIS)
// 表: 95 5C (Shift_JIS)
// /: 5C (Shift_JIS)

反思一下,如果要支持国际化,最正确也最简单的办法是都编译成Unicode程序。
当然这样就很有可能需要进行些字符集的转换,整理了4个函数。
还可以组合使用,比如要多字节字符串-->UTF8格式字符串,可以调用1)+3)。

1)当前系统编码的多字节字符串-->Unicode格式字符串

bool multiToUnicode(const std::string& multiText, std::wstring& unicodeText)
{
	if (0 == multiText.length())
	{
		unicodeText.clear();
		return true;
	}
	
	//先获取转换后字符串所需空间
	int size = ::MultiByteToWideChar(CP_ACP, 0, multiText.c_str(), -1, NULL, 0);
	if (0 == size)
	{
		return false;
	}
	
	//分配空间,进行转换
	wchar_t* wszBuffer = new wchar_t[size + 1];
	::ZeroMemory(wszBuffer, (size + 1) * sizeof(wchar_t));
	if (0 == ::MultiByteToWideChar(CP_ACP, 0, multiText.c_str(), -1, wszBuffer, size + 1))
	{
		delete[] wszBuffer;
		return false;
	}
	
	unicodeText = wszBuffer;
	delete[] wszBuffer;
	return true;
}


2)Unicode格式字符串-->当前系统编码的多字节字符串

bool unicodeToMulti(const std::wstring& unicodeText, std::string& multiText)
{
	if (0 == unicodeText.length())
	{
		multiText.clear();
		return true;
	}
	
	//先获取转换后字符串所需空间
	int size = ::WideCharToMultiByte(CP_ACP, 0, unicodeText.c_str(), -1, NULL, 0, NULL, NULL);
	if (0 == size)
	{
		return false;
	}
	
	//分配空间,进行转换
	char* szBuffer = new char[size + 1];
	::ZeroMemory(szBuffer, (size + 1) * sizeof(char));
	if (0 == ::WideCharToMultiByte(CP_ACP, 0, unicodeText.c_str(), -1, szBuffer, size + 1, NULL, NULL))
	{
		delete[] szBuffer;
		return false;
	}
	
	multiText = szBuffer;
	delete[] szBuffer;
	return true;
}


3)Unicode格式字符串-->UTF8格式字符串

bool unicodeToUtf8(const std::wstring& unicodeText, std::string& utf8Text)
{
	if (0 == unicodeText.length())
	{
		utf8Text.clear();
		return true;
	}
	
	//先获取转换后字符串所需空间
	int size = ::WideCharToMultiByte(CP_UTF8, 0, unicodeText.c_str(), -1, NULL, 0, NULL, NULL);
	if (0 == size)
	{
		return false;
	}
	
	//分配空间,进行转换
	char* szBuffer = new char[size + 1];
	::ZeroMemory(szBuffer, (size + 1) * sizeof(char));
	if (0 == ::WideCharToMultiByte(CP_UTF8, 0, unicodeText.c_str(), -1, szBuffer, size + 1, NULL, NULL))
	{
		delete[] szBuffer;
		return false;
	}
	
	utf8Text = szBuffer;
	delete[] szBuffer;
	return true;
}


4)UTF8格式字符串-->Unicode格式字符串

bool utf8ToUnicode(const std::string& utf8Text, std::wstring& unicodeText)
{
	if (0 == utf8Text.length())
	{
		unicodeText.clear();
		return true;
	}
	
	//先获取转换后字符串所需空间
	int size = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.c_str(), -1, NULL, 0);
	if (0 == size)
	{
		return false;
	}
	
	//分配空间,进行转换
	wchar_t* wszBuffer = new wchar_t[size + 1];
	::ZeroMemory(wszBuffer, (size + 1) * sizeof(wchar_t));
	if (0 == ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.c_str(), -1, wszBuffer, size + 1))
	{
		delete[] wszBuffer;
		return false;
	}
	
	unicodeText = wszBuffer;
	delete[] wszBuffer;
	return true;
}


补充1)
_UNICODE vs UNICODE
在学习UNICODE的过程中发现有两种关于UNICODE的宏定义: UNICODE和_UNICODE.

UNICODE:
这个宏主要是在Windows SKD中使用, 比如GetWindowText(), 定义了UNICODE以后将被定义为GetWindowTextW(UNICODE版本),否则被定义成GetWindowTextA(ANSI版本).

_UNICODE:
该宏一般用在C运行时库和MFC头文件中, 这时候函数_tcslen()将被映射为wcslen(), 反之被映射为strlen(). 至于在MFC中, 一般存在如下的定义:
#ifdef _UNICODE
#ifndef UNICODE
#define UNICODE
#endif
#endif

#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE
#endif
#endif
所以在MFC使用哪个都可以.

总结:两个都用上总不会有错的:)

补充2)
VC6中为了编译Unicode软件出了需要添加_UNICODE宏定义外,还需要增加一步:指定程序的入口位置。
否则程序将会发生如下错误:error LNK2001: unresolved external symbol _WinMain@16。
解决办法:在Project Settings > Entry-point symbol编辑框中输入wWinMainCRTStartup。

补充3)
C++比Unicode出生得早,所以最开始没有考虑Unicode支持,char类型是单字节的。
wchar_t实际上是typedef定义。
所以特别要注意防止下面错误。
CString sText = _T("123456789");
int nLength = sText.getLength() //i=9
CFile cFile;
......
cFile.Write(sText,sText.getLength());
这段代码再普通不过了,编译也不会有任何问题。但是实际上输出的内容将会是错误的。正确的代码应该如下:
CString sText = _T("123456789");
int nLength = sText.getLength() //i=9
CFile cFile;
......
cFile.Write(sText,sText.getLength()×sizeof(_TCHAR));

补充4)
Java设计的时候已经考虑了对unicode的支持,所以Java的char类型占用2个字节。
Java中如果要进行字符集转换就简单多了,写了个程序测试了一下。

/*
 * Created on 2011/01/04
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */

package learning.io;

import java.io.UnsupportedEncodingException;
import java.util.Locale;

// import learning.util.StringToCharSequence;

/**
 * @author
 * 
 * TODO To change the template for this generated type comment go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
public class CharEncodingTest {

	public static void main(String[] args) throws UnsupportedEncodingException {
		System.out.println(Locale.getDefault().getDisplayCountry());
		System.out.println(System.getProperty("file.encoding"));

		// ///
		// Unicode -> UTF8
		// 06543210 0xxxxxxx
		// 110(10)9876 10543210 110xxxxx 10xxxxxx
		// 1110(15)(14)(13)(12) 10(11)(10)9876 10543210 1110xxxx 10xxxxxx
		// 10xxxxxx
		// ///

		// ///
		// 項目表\
		// 項: unicode, int=38917 \u9805
		// 目: unicode, int=30446 \u76EE
		// 表: unicode, int=34920 \u8868
		// \: unicode, int=92 \u005C
		//
		// 項: E9 A0 85 (UTF8, 9805 --> E9 A0 85)
		// 目: E7 9B AE (UTF8, 76EE --> E7 9B AE)
		// 表: E8 A1 A8 (UTF8, 8868 --> E8 A1 A8)
		// \:  5C	(UTF8, 005C --> 5C)
		
		// 項: 8D 80 (Shift_JIS)
		// 目: 96 DA (Shift_JIS)
		// 表: 95 5C (Shift_JIS)
		// \: 5C (Shift_JIS)
		// ///

		// ///
		// 项目表\
		// 项: unicode, int=39033 \u9879
		// 目: unicode, int=30446 \u76EE
		// 表: unicode, int=34920 \u8868
		// \: unicode, int=92 \u005C
		//
		// 项: E9 A1 B9 (UTF8, 9879 --> E9 A1 B9)
		// 目: E7 9B AE (UTF8, 76EE --> E7 9B AE)
		// 表: E8 A1 A8 (UTF8, 8868 --> E8 A1 A8)
		// \:  5C	(UTF8, 005C --> 5C)
		
		// 项: CF EE (GBK)
		// 目: C4 BF (GBK)
		// 表: B1 ED (GBK)
		// \: 5C (GBK)
		// ///

		parseString("項目表\\");
		
		// "ABAB"
		String strUTF8 = String.valueOf(new char[] {0xEF, 0xBC, 0xA1, 0xEF, 0xBC, 0xA2, 0x41, 0x42});
		String strJP = String.valueOf(new char[] {0x82, 0x60, 0x82, 0x61, 0x41, 0x42});
		String strCN = String.valueOf(new char[] {0xA3, 0xC1, 0xA3, 0xC2, 0x41, 0x42});
		
		// "UTF8" -> "Unicode" 
		String strUnicode1 = new String(strUTF8.getBytes("ISO-8859-1"), "UTF8");
		
		// "Shift_JIS" -> "Unicode" 
		String strUnicode2 = new String(strJP.getBytes("ISO-8859-1"), "Shift_JIS");
		
		// "GBK" -> "Unicode" 
		String strUnicode3 = new String(strCN.getBytes("ISO-8859-1"), "GBK");
		
		System.out.println(strUnicode1);
		System.out.println(strUnicode2);
		System.out.println(strUnicode3);
	}

	public static void parseString(String strTest) throws UnsupportedEncodingException {
		printCharArray(strTest);

		System.out.println("UTF8:");
		byte a[] = strTest.getBytes("UTF8");
		printByteArray(a);

		System.out.println("Shift_JIS:");
		byte b[] = strTest.getBytes("Shift_JIS");
		printByteArray(b);

		System.out.println("GBK:");
		byte c[] = strTest.getBytes("GBK"); // Encode into "GBK"
		printByteArray(c);
	}

	public static void printByteArray(byte[] bytes) {
		for (int i = 0; i < bytes.length; i++) {
			String hex = Integer.toHexString(bytes[i]).toUpperCase();
			StringBuffer sb = new StringBuffer();

			// print byte
			sb.append("byte[");
			sb.append(i);
			sb.append("]='");
			sb.append(bytes[i]);
			sb.append("'\t");

			// short value
			sb.append(hex);
			sb.append('\t');

			System.out.println(sb.toString());
		}

		System.out.println();
	}

	public static void printCharArray(String inStr) {
		char[] myBuffer = inStr.toCharArray();

		// list each Charactor in byte value, short value,
		// and UnicodeBlock Mapping
		for (int i = 0; i < inStr.length(); i++) {
			int s = (int) myBuffer[i];
			String hexS = Integer.toHexString(s).toUpperCase();
			StringBuffer sb = new StringBuffer();

			// print char
			sb.append("char[");
			sb.append(i);
			sb.append("]='");
			sb.append(myBuffer[i]);
			sb.append("'\t");

			// int value
			sb.append("int=");
			sb.append(s);
			sb.append(" \\u");
			sb.append(hexS);
			sb.append('\t');

			// Unicode Block
			sb.append(Character.UnicodeBlock.of(myBuffer[i]));

			System.out.println(sb.toString());
		}

		System.out.println();
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值