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}呢?
我们处理的方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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; } |
根据传入的代码转换成相应字节流。而后概据我们的处理逻辑编写代码进行替换。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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; } |
而一个特殊字符的编码如何取得可以跟据以下代码自己计算,代码如下:
1 2 3 4 5 6 7 8 9 | 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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架