有一个文本文件"test.txt",包含3行数据,如下图:
图1
其16进制码如下图所示:
图2
现在要用C#中StreamReader类来读取该文本文件,代码如下:
StreamReader sr = new StreamReader(sFileName, Encoding.Unicode);//,sFileName是要读取的文本文件名
string sText = sr.ReadLine();//读取一行数据
if (sText == null)
{
return;//读取数据空,返回
}
this.txtText.Text = sText;//显示读取的字符串
sr.Close();
byte[] byteArr = encode.GetBytes(sText);//转化为字节数组
int nLength=byteArr.Length;//字节数组长度
string sByte = "";
if (nLength>0)
{
sByte = Convert.ToString(byteArr[0], 16);//转换成16进制
if (nLength>1)
{
for (int i=1;i<byteArr.Length;i++)
{
sByte += ",";//用逗号隔开
sByte += Convert.ToString(byteArr[i], 16);//转换成16进制
}
}
}
this.txtBytesArray.Text = sByte;//显示对应的16进制
结果如下:
图3
读取的字符串是“愱탖調愲탖調愳탖調”,这是乱码、明显不对。但是其16进制字节数组是:“31,61,d6,d0,b9,fa,d,a,32,61,d6,d0,b9,fa,d,a,33,61,d6,d0,b9,fa”,这和图2中显示的一致(连回车换行符0d,0a都有),说明读取的字节数没错,对不对?But,有一个问题。这不是三行数据吗?!而我用的函数ReadLine()理论上应该是只读取一行数据啊?难道ReadLine()不只读取一行?而是三行?为了验证它到底读取多少行数据,我又将1.txt中数据改为5行,结果还是读取了全部的5行;我又将1.txt中数据改为100行,结果还是读取了全部的100行。以此类推,ReadLine()是读取了文件全部的数据,而不是一行。
为了证明在其他编码方式下ReadLine()是不是读取一行,我又尝试了其他的编码,Encoding.Default,结果如下:
图4
如图,用Encoding.Default读取的字符串和字节数组显示都是第一行的数据,没错,ReadLine()是读取一行。
为什么用Encoding.Unicode编码读取文本,ReadLine()不是读取一行,而是读取全部呢?
如果使用Encoding.Unicode编码读取文本,怎么才能逐行读取呢?
恳请大家帮忙解答其中原因。
上面那个问题算是有了答案,但是又有另外一个问题。
现有文本文件“test.txt”(使用vc保存的),只有2行字符串,如下图所示:
图1
其行数据是按照一定格式排列的,前6个字节表示是人名(字符串),后2个字节表示年龄(整形值)。例如第一行“熊选文28”,表示熊选文的年龄是28岁。注意,第二行其中的”?”不是中文的问号,也不是英文的问号,只是没有对应字符,才显示? 。如下图,该?对应的两个字节:C6 32。
图2
现在要求用C#编写程序来读上面这个文本文件,并将每行的人名和年龄都存储起来。
很简单,用StreamReader就可以,代码如下:
public void ReadFileData(string fileName)
{
//System.Text.Encoding encode=System.Text.Encoding.Default;
System.Text.Encoding encode=System.Text.Encoding.GetEncoding("gb2312");
//default表示使用操作系统的编码即可,一般中文操作系统都是Encoding.GetEncoding("gb2312"),但是其他系统就不一样了
//所以,此处其实用Encoding.GetEncoding("gb2312")更精确一些,免得有时候换成英文或其他操作系统,读取数据就有问题了
using(StreamReader sr = new StreamReader(fileName, encode))
{
if (sr == null)
{
return;
}
int nRow=0;//行号
string sLineBuf = null;//行数据缓存
string sName = "";//人名
int nAge = 0;// 年龄
while ((sLineBuf = sr.ReadLine()) != null)//
{
nRow++;
if (sLineBuf == "")
continue;//如果为空字符串,跳过当前行
sName =GetSubString(sLineBuf, 0, 6);//读取人名(前6个字节)
string sAge =GetSubString(sLineBuf, 6, 2);//后2个字节
nAge = Convert.ToInt16(sAge);//读取年龄
}
}
}
因为字符串自带的取子字符串的函数Substring()是按字符数来截取的,而不是按照字节数来截取。故我自己写了一个按照字节数来截取子字符串的函数:GetSubString。
/// <summary>
/// 从一个字符串中某个位置(该位置以字节数而不是字符数计算)开始取定长(该字符串的长度以字节数而不是字符数计算)的字符串
/// </summary>
/// <param name="SStr">源字符串</param>
/// <param name="nStart">取字符串的起始位置</param>
/// <param name="nByte">字符串的长度</param>
/// <returns>返回字符串</returns>
public static string GetSubString(string SStr,int nStart,int nByte)
{
string Tstr = "";
byte[] sbytes = System.Text.Encoding.GetEncoding("gb2312").GetBytes(SStr);//转换为字节数组
if (sbytes.Length == 0)
return Tstr;
if (nStart > sbytes.Length)
return Tstr;
byte[] tbytes = new byte[nByte];
int i = nStart;
int j = 0;
while (i < sbytes.Length && j < nByte)
{
tbytes[j++] = sbytes[i++];
}
try
{
Tstr = System.Text.Encoding.GetEncoding("gb2312").GetString(tbytes);//转换为字符串
}
catch (System.Exception ex)
{
throw ex;
}
return Tstr;
}
上面的程序写好了,开始读取数据,输出结果如下:
人名:年龄
熊选文:28
张三?2:0
第一行数据是没问题,但第二行就有问题了。本来应该是"张三?"的年龄是"20"岁,读出来的结果是"张三?2"的年龄是"0"岁,明显有误。为什么会出现这样的结果呢?
通过调试,就会发现c#读取该行的十六进制为:D5 C5 C8 FD 3F 32 30,而原本应该是D5 C5 C8 FD C6 32 32 30(见图2)。原本的C6 32变成了3F(3F对应的字符就是英文的问号),所以字节数就少了一个,截取前6个字节就是D5 C5 C8 FD 3F 32,对应的字符串就是“张三?2”,后面只有一个字节30,对应的字符就是0。所以最后得到的结果是“张三?2”的年龄是“0”。
通过对比分析发现,第一行数据中没有像“C6 32”这样的字符,所以用c#读出来结果很正常。第二行中有“C6 32”这样的字符,用C#读就会自动转换为3F。如果把“C6 32”换成其他的字符,如“BD 32”,都会有同样的问题。这是为什么呢?
像上面这种文本数据,用C#如何才能无误的读取数据呢(按行读取,然后按照格式截取字符串)?
用Encoding.Default.EncodingName 看一下?通常是GB2312的吧。
嗯,国内的机子一般都是中文操作系统,所以一般都是GB2312,我测试过。但是我想问的是,为何用Encoding.Unicode编码读取文本,ReadLine()读取的不是一行,而是读取全部呢?
文本文件是什么编码?
不知道编码格式。假设就是不知道格式,才用不同的方法读取看看有什么不同。
@Kevin.Xiong: 将文本文件保存为UTF-8的格式试试
其实这个问题可能没什么意义。因为我不知道这个文本文件是用什么字符编码(不过上面的文本文件肯定不是unicode编码),所以用了几种不同的编码方式来读。因为不同的编码的规则不一样。就拿回车换行符"\r\n"来说,使用GB2312格式存储"\r\n"的字符是"0D 0A",Unicode则是"00 0D 00 0A"。所以如果是用GB2312保存的文本文件,回车换行符是"0D 0A"。用Unicode来读,它遇到回车换行符"0D 0A",并不会把它当作回车换行符,它只会把"00 0D 00 0A"当作回车换行符,故ReadLine()就不起作用。
归根结底,还是字符编码的问题。
最近也在纠结这个问题,看了你的问题,我的理解(个人):
如果你的编码和解码不是对应的,那肯定是不对的。
一般情况下,编码和解码都是一致的。如:用notepad生成一个txt,notepad是默认采用当前codepage进行编码,当你将txt读入时,C#也是默认将读进来的字节流以当前codepage进行解码。
但是你在读入字节流后,采用的endcoding.unicode解码,肯定是乱码的。
没有识别回车换行符,先看是用什么编码的,然后在解码