首页 新闻 赞助 找找看

c#使用Encoding.Unicode编码读取文本,ReadLine()不是读取一行,而是读取全部,why ?

0
悬赏园豆:50 [待解决问题]

有一个文本文件"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变成了3F3F对应的字符就是英文的问号),所以字节数就少了一个,截取前6个字节就是D5 C5 C8 FD 3F 32,对应的字符串就是“张三?2”,后面只有一个字节30,对应的字符就是0。所以最后得到的结果是“张三?2”的年龄是“0”。

通过对比分析发现,第一行数据中没有像“C6 32”这样的字符,所以用c#读出来结果很正常。第二行中有“C6 32”这样的字符,用C#读就会自动转换为3F。如果把“C6 32”换成其他的字符,如“BD 32”,都会有同样的问题。这是为什么呢?

像上面这种文本数据,用C#如何才能无误的读取数据呢(按行读取,然后按照格式截取字符串)?

先之的主页 先之 | 初学一级 | 园豆:147
提问于:2012-02-29 20:47
< >
分享
所有回答(4)
0

用Encoding.Default.EncodingName  看一下?通常是GB2312的吧。

Firen | 园豆:5385 (大侠五级) | 2012-02-29 21:45

嗯,国内的机子一般都是中文操作系统,所以一般都是GB2312,我测试过。但是我想问的是,为何用Encoding.Unicode编码读取文本,ReadLine()读取的不是一行,而是读取全部呢?

支持(0) 反对(0) 先之 | 园豆:147 (初学一级) | 2012-03-01 08:56
0

文本文件是什么编码?

dudu | 园豆:31075 (高人七级) | 2012-03-01 11:11

不知道编码格式。假设就是不知道格式,才用不同的方法读取看看有什么不同。

支持(0) 反对(0) 先之 | 园豆:147 (初学一级) | 2012-03-01 14:20

@Kevin.Xiong: 将文本文件保存为UTF-8的格式试试

支持(0) 反对(0) dudu | 园豆:31075 (高人七级) | 2012-03-01 14:28
1

其实这个问题可能没什么意义。因为我不知道这个文本文件是用什么字符编码(不过上面的文本文件肯定不是unicode编码),所以用了几种不同的编码方式来读。因为不同的编码的规则不一样。就拿回车换行符"\r\n"来说,使用GB2312格式存储"\r\n"的字符是"0D 0A",Unicode则是"00 0D 00 0A"。所以如果是用GB2312保存的文本文件,回车换行符是"0D 0A"。用Unicode来读,它遇到回车换行符"0D 0A",并不会把它当作回车换行符,它只会把"00 0D 00 0A"当作回车换行符,故ReadLine()就不起作用。

归根结底,还是字符编码的问题。

先之 | 园豆:147 (初学一级) | 2012-03-01 14:30

 最近也在纠结这个问题,看了你的问题,我的理解(个人):

如果你的编码和解码不是对应的,那肯定是不对的。

一般情况下,编码和解码都是一致的。如:用notepad生成一个txt,notepad是默认采用当前codepage进行编码,当你将txt读入时,C#也是默认将读进来的字节流以当前codepage进行解码。

但是你在读入字节流后,采用的endcoding.unicode解码,肯定是乱码的。

支持(0) 反对(0) CX_ | 园豆:75 (初学一级) | 2015-08-17 10:54
0

没有识别回车换行符,先看是用什么编码的,然后在解码

OOLi | 园豆:163 (初学一级) | 2012-03-05 12:13
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册