C# utf-8编码时转换成shift-jis时出现乱码问题的处理
最近在做项目时遇到导出CSV文件时,因客户方要求导出CSV文件一定要是shift-jis编码的CSV文件,而我们数据库存储时是unicode储存的,所以导出时会有很多?的编码,这是因为:
借住码表来解释:
Shift_JIS | |||||||||||||||||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | ||
00 | NUL | SOH | STX | ETX | EOT | ENQ | ACK | BEL | BS | HT | LF | VT | FF | CR | SO | SI | |
10 | DLE | DC1 | DC2 | DC3 | DC4 | NAK | SYN | ETB | CAN | EM | SUB | ESC | FS | GS | RS | US | |
20 | SP | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / | |
30 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? | |
40 | @ | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | |
50 | P | Q | R | S | T | U | V | W | X | Y | Z | [ | ¥ | ] | ^ | _ | |
60 | ` | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | |
70 | p | q | r | s | t | u | v | w | x | y | z | { | | | } | ~ | DEL | |
80 | |||||||||||||||||
90 | |||||||||||||||||
A0 | 。 | 「 | 」 | 、 | ・ | ヲ | ァ | ィ | ゥ | ェ | ォ | ャ | ュ | ョ | ッ | ||
B0 | ー | ア | イ | ウ | エ | オ | カ | キ | ク | ケ | コ | サ | シ | ス | セ | ソ | |
C0 | タ | チ | ツ | テ | ト | ナ | ニ | ヌ | ネ | ノ | ハ | ヒ | フ | ヘ | ホ | マ | |
D0 | ミ | ム | メ | モ | ヤ | ユ | ヨ | ラ | リ | ル | レ | ロ | ワ | ン | ゙ | ゚ | |
E0 | |||||||||||||||||
F0 |
Shift_JIS是一个日本电脑系统常用的编码表。它能容纳全形及半形拉丁字母、平假名、片假名、符号及日语汉字。
它被命名为Shift_JIS的原因,是它在放置全形字符时,要避开原本在0xA1-0xDF放置的半角假名字符。
在微软及IBM的日语电脑系统中,即使用了这个编码表。这个编码表称为CP932。
字节结构
以下字元在Shift_JIS使用一个字节来表示。
ASCII字符 (0x20-0x7E),但“/”被“¥”取代
ASCII控制字符 (0x00-0x1F、0x7F)
JIS X 0201标准内的半角标点及片假名(0xA1-0xDF)
在部分操作系统中,0xA0用来放置“不换行空格”。
以下字元在Shift_JIS使用两个字节来表示。
JIS X 0208字集的所有字符
“第一位字节”使用0x81-0x9F、0xE0-0xEF (共47个)
“第二位字节”使用0x40-0x7E、0x80-0xFC (共188个)
使用者定义区
“第一位字节”使用0xF0-0xFC (共47个)
“第二位字节”使用0x40-0x7E、0x80-0xFC (共188个)
在Shift_JIS编码表中,并未使用0xFD、0xFE及0xFF。
在微软及IBM的日语电脑系统中,在0xFA、0xFB及0xFC的两字节区域,加入了388个JIS X 0208没有收录的符号和汉字。
因为unicode的很多编码而shift-jis并没有用到,所以在转换时shift-jis没有对应的编码转换,所以转换成byte时都是以63来代替,即是?显示出来,因些我们要跟据原来字符串的字节码所对应的字符替换成shift-jis能显的相应字符。
我们的设计思路如下:
1、用一张转换表来处理保存要替换的编码表和字符表。
2、用两种处理方式来处理转换代码。
a:用编码来替换,有些特殊字符并没显示出字符串,但是他却是存在的,如空字符,0xa0,shift-jis里并没有对应的编码。还有一些特殊字符,如utf-8是new byte[] {0xef, 0xbb,0xbf}的空字符串。
b:在字符串转换前替换掉。如一些明显可保存的字付串。如〜替换成~,直接Replace替换掉.
问题就会随之而来,我们在表时只能保存像 0xef, 0xbb,0xbf 这样的字符串,怎么样转换成new byte[] {0xef, 0xbb,0xbf}呢?
我们处理的方式如下:
private byte[] ConvertStringToByte(string originalStr) { if (string.IsNullOrEmpty(originalStr)) return null; string[] originalSplit = originalStr.Split(','); int originalFirstValue = 0, originalSecondValue = 0, originalThirdValue = 0; byte[] resultByte; originalFirstValue = Convert.ToInt32(originalSplit[0].Trim(), 16); if (originalSplit.Length == 2) { originalSecondValue = Convert.ToInt32(originalSplit[1].Trim(), 16); resultByte = new byte[] { BitConverter.GetBytes(originalFirstValue)[0], BitConverter.GetBytes(originalSecondValue)[0] }; } else if (originalSplit.Length == 3) { originalSecondValue = Convert.ToInt32(originalSplit[1].Trim(), 16); originalThirdValue = Convert.ToInt32(originalSplit[2].Trim(), 16); resultByte = new byte[] { BitConverter.GetBytes(originalFirstValue)[0], BitConverter.GetBytes(originalSecondValue)[0], BitConverter.GetBytes(originalThirdValue)[0] }; } else { resultByte = new byte[] { BitConverter.GetBytes(originalFirstValue)[0] }; } return resultByte; }
根据传入的代码转换成相应字节流。而后概据我们的处理逻辑编写代码进行替换。
代码如下:
public string ReplaceString(string content) { List<MessyCodeHandleBE> messyCodeHandleBEList = RetrieveAll(); foreach (MessyCodeHandleBE entity in messyCodeHandleBEList) { if (entity.ConvertType == MessyCodeHandleConvertTypeChoices.ENCODEREPLACE) { content = content.Replace(Encoding.UTF8.GetString(ConvertStringToByte(entity.OriginalCode)), entity.ReplaceCode); } else { content = content.Replace(entity.OriginalCode, entity.ReplaceCode); } } return content; }
而一个特殊字符的编码如何取得可以跟据以下代码自己计算,代码如下:
private string ConvertToShiftJis(string content) { Encoding orginal = Encoding.GetEncoding("utf-8"); Encoding ShiftJis = Encoding.GetEncoding("Shift-JIS"); byte[] unf8Bytes = orginal.GetBytes(content); byte[] myBytes = Encoding.Convert(orginal, ShiftJis, unf8Bytes); string JISContent = ShiftJis.GetString(myBytes); return JISContent; }
在调试时查看其字节编码,如图:
而239的16进制是0xef,187的16进制是0xbb,191的16进制是0xbf.
总结
就是查找字符串对应shift-jis编码为63时对应的byte[]字节是什么,再用Replace替换掉就OK了。如果你有什么新的发现,欢迎留言交流。
作者:spring yang
出处:http://www.cnblogs.com/springyangwc/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。