• 推荐
  • 评论
  • 收藏

C# utf8编码时转换成shiftjis时出现乱码问题的处理

2022-12-10    1796次浏览

最近在做项目时遇到导出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}呢?

我们处理的方式如下:

01 private byte[] ConvertStringToByte(string originalStr)
02 {
03     if (string.IsNullOrEmpty(originalStr)) return null;
04     string[] originalSplit = originalStr.Split(',');           
05     int originalFirstValue = 0, originalSecondValue = 0, originalThirdValue = 0;
06     byte[] resultByte;
07     originalFirstValue = Convert.ToInt32(originalSplit[0].Trim(), 16);
08     if (originalSplit.Length == 2)
09     {
10         originalSecondValue = Convert.ToInt32(originalSplit[1].Trim(), 16);
11         resultByte = new byte[] { BitConverter.GetBytes(originalFirstValue)[0], BitConverter.GetBytes(originalSecondValue)[0] };
12     }
13     else  if (originalSplit.Length == 3)
14     {
15         originalSecondValue = Convert.ToInt32(originalSplit[1].Trim(), 16);
16         originalThirdValue = Convert.ToInt32(originalSplit[2].Trim(), 16);
17         resultByte = new byte[] { BitConverter.GetBytes(originalFirstValue)[0], BitConverter.GetBytes(originalSecondValue)[0], BitConverter.GetBytes(originalThirdValue)[0] };
18     }
19     else
20     {
21         resultByte = new byte[] { BitConverter.GetBytes(originalFirstValue)[0] };
22     }
23     return resultByte;
24 }

根据传入的代码转换成相应字节流。而后概据我们的处理逻辑编写代码进行替换。

代码如下:

01 public string ReplaceString(string content)
02  {
03      List<MessyCodeHandleBE> messyCodeHandleBEList = RetrieveAll();
04  
05      foreach (MessyCodeHandleBE entity in messyCodeHandleBEList)
06      {
07          if (entity.ConvertType == MessyCodeHandleConvertTypeChoices.ENCODEREPLACE)
08          {
09              content = content.Replace(Encoding.UTF8.GetString(ConvertStringToByte(entity.OriginalCode)), entity.ReplaceCode);
10          }
11          else
12          {
13              content = content.Replace(entity.OriginalCode, entity.ReplaceCode);
14          }
15      }
16      return content;
17  }

而一个特殊字符的编码如何取得可以跟据以下代码自己计算,代码如下:

1 private string ConvertToShiftJis(string content)
2 {
3     Encoding orginal = Encoding.GetEncoding("utf-8");
4     Encoding ShiftJis = Encoding.GetEncoding("Shift-JIS");
5     byte[] unf8Bytes = orginal.GetBytes(content);
6     byte[] myBytes = Encoding.Convert(orginal, ShiftJis, unf8Bytes);
7     string JISContent = ShiftJis.GetString(myBytes);
8     return JISContent;
9 }

在调试时查看其字节编码,如图:

image

而239的16进制是0xef,187的16进制是0xbb,191的16进制是0xbf.

总结

就是查找字符串对应shift-jis编码为63时对应的byte[]字节是什么,再用Replace替换掉就OK了。如果你有什么新的发现,欢迎留言交流。

作者:spring yang

出处:http://www.cnblogs.com/springyangwc/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文地址:https://www.cnblogs.com/Leo_wl/p/2099591.html