码迷,mamicode.com
首页 > Windows程序 > 详细

C#中的字符编码问题

时间:2015-07-31 07:51:24      阅读:151      评论:0      收藏:0      [点我收藏+]

标签:

该文件的编码为GB18030,每行的宽度为23个字符,其中第1-8列为员工姓名,第10-23列为工资额。现在我们要写一个C#程序求出该单位员工的平均工资,如下所示:

 1技术分享using System; 
 2技术分享using System.IO; 
 3技术分享using System.Text; 
 4技术分享 
 5技术分享namespace Skyiv.Ben.Test 
 6技术分享
 7技术分享  sealed class Avg 
 8技术分享  
 9技术分享    static void Main() 
10技术分享    
11技术分享      try 
12技术分享      
13技术分享        Encoding encode = Encoding.GetEncoding("GB18030"); 
14技术分享        using (StreamReader sr = new StreamReader("salary.txt", encode)) 
15技术分享        
16技术分享          decimal avg = 0
17技术分享          long rows = 0
18技术分享          for (; ; rows++
19技术分享          
20技术分享            string s = sr.ReadLine(); 
21技术分享            if (s == nullbreak
22技术分享            decimal salary = Convert.ToDecimal(s.Substring(914)); 
23技术分享            avg += salary; 
24技术分享          }
 
25技术分享          avg /= rows; 
26技术分享          Console.WriteLine(avg.ToString("N2")); 
27技术分享        }
 
28技术分享      }
 
29技术分享      catch (Exception ex) 
30技术分享      
31技术分享        Console.WriteLine("错误: " + ex.Message); 
32技术分享      }
 
33技术分享    }
 
34技术分享  }
 
35技术分享}
 
36技术分享

  运行结果如下所示: 
错误: 索引和长度必须引用该字符串内的位置 
参数名: length 
  稍一分析(或者使用debug工具),就知道是该程序的第22行出错: 
  decimal salary = Convert.ToDecimal(s.Substring(9, 14)); 
  实际上,C#中的string的编码是Unicode,每个全角的汉字也只能算一个字符,所以salary.txt中的第一行只有20个字符,第二行是21个字符,第三行是19个字符,均没有达到23个字符,所以s.Substring(9, 14)会抛出异常。实际上,只要把这一行改为以下语句就行了: 
  decimal salary = Convert.ToDecimal(encode.GetString(encode.GetBytes(s), 9, 14)); 
  重新编译后再运行就可以得到正确的结果了: 329,218,792.83。 
  其实,更好的办法是把该程序的13-27行替换为以下语句:

技术分享        const int bytesPerRow = 23 + 2
技术分享        Encoding encode 
= Encoding.GetEncoding("GB18030"); 
技术分享        
using (BinaryReader br = new BinaryReader(new FileStream("salary.txt", FileMode.Open))) 
技术分享        

技术分享          
if (br.BaseStream.Length % bytesPerRow != 0throw new Exception("文件长度错"); 
技术分享          
decimal avg = 0
技术分享          
long rows = br.BaseStream.Length / bytesPerRow; 
技术分享          
for (long i = 0; i < rows; i++
技术分享          

技术分享            
byte [] bs = br.ReadBytes(bytesPerRow); 
技术分享            
decimal salary = Convert.ToDecimal(encode.GetString(bs, 914)); 
技术分享            avg 
+= salary; 
技术分享          }
 
技术分享          avg 
/= rows; 
技术分享          Console.WriteLine(avg.ToString(
"N2")); 
技术分享        }
 
技术分享


 

  现在,假设我们的任务是生成salary.txt,以下程序能工作吗?

 1技术分享using System; 
 2技术分享using System.IO; 
 3技术分享using System.Text; 
 4技术分享 
 5技术分享namespace Skyiv.Ben.Test 
 6技术分享
 7技术分享  sealed class Salary 
 8技术分享  
 9技术分享    static void Main() 
10技术分享    
11技术分享      try 
12技术分享      
13技术分享        Encoding encode = Encoding.GetEncoding("GB18030"); 
14技术分享        string [] names = {"李富贵""容闳""欧阳吹雪"}
15技术分享        decimal [] salarys = {0.01m2057.38m987654321.09m}
16技术分享        using (StreamWriter sw = new StreamWriter("salary.txt"false, encode)) 
17技术分享        
18技术分享          for (int i = 0; i < names.Length; i++
19技术分享            sw.WriteLine("{0,-8} {1,14:N2}", names[i], salarys[i]); 
20技术分享        }
 
21技术分享      }
 
22技术分享      catch (Exception ex) 
23技术分享      
24技术分享        Console.WriteLine("错误: " + ex.Message); 
25技术分享      }
 
26技术分享    }
 
27技术分享  }
 
28技术分享}
 
29技术分享

  运行结果表明生成的文件中各行的宽度长短不一。怎么办呢?只要把程序中第19行改为: 
  sw.WriteLine("{0} {1,14:N2}", encode.GetString(encode.GetBytes(names[i].PadRight(8)), 0, 8), salarys[i]); 
  就行了。

  假如salary.txt文件的编码是UTF-16,是否把程序中的 
  Encoding encode = Encoding.GetEncoding("GB18030"); 
  改为: 
  Encoding encode = Encoding.Unicode; 
  就可以了呢?这个问题就留给读者们去思考了。

  设想一下,如果在不远的将来能够实现在所有的操作系统中,字符编码都采用UTF-16,并且一个全角字符和一个半角在屏幕显示和在打印机上打印出来时所占的宽度都一样的(等宽字体的情况下,如果不是等宽字体,半角的A和i所占的宽度也不一样)。这时,也就不需要全角和半角概念了(反正大家都一样,全角也就是半角),也就不存在本文中所讨论的问题了,就象现在英语国家的程序员不会有这个问题一样(对他们来说根本就不存在全角字符的概念)。

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

C#中的字符编码问题

标签:

原文地址:http://blog.csdn.net/u013948187/article/details/47163969

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!