首页 > 开发 > 综合 > 正文

[C#]解决读写包含汉字的txt文件时乱码的问题

2024-07-21 02:17:28
字体:
来源:转载
供稿:网友
,欢迎访问网页设计爱好者web开发。作者:袁晓辉(版权所有)时间:2005-8-8   当我们用system.io.streamreader读取包含汉字的txt文件时,经常会读出乱码(streamwriater写文本文件也有类似的问题),原因很简单,就是文件的编码(encoding)和streamreader/writer的encoding不对应。    为了解决这个问题,我写了一个类,来取得一个文本文件的encoding,这样我们就可以创建对应的streamreader和streamwriter来读写,保证不会出现乱码现象。其实原理很简单,文本编辑器(比如xp自带的记事本)在生成文本文件时,如果编码格式和系统默认的编码(中文系统下默认为gb2312)不一致时,会在txt文件开头部分添加特定的“编码字节序标识(encoding bit order madk,简写为bom)”,类似pe格式的"mz"文件头。这样它在读取时就可以根据这个bom来确定该文本文件生成时所使用的encoding。这个bom我们用记事本等程序打开默认是看不到的,但是用stream按字节读取时是可以读到的。我的这个txtfileencoding类就是根据这个bom“文件头”来确定txt文件生成时用到的编码的。

//  作者:袁晓辉

//  2005-8-8

// // // // // //

using system;

using system.text;

using system.io;

namespace farproc.text

{

    /// <summary>

    /// 用于取得一个文本文件的编码方式(encoding)。

    /// </summary>

    public class txtfileencoding

    {

        public txtfileencoding()

        {

            //

            // todo: 在此处添加构造函数逻辑

            //

        }

        /// <summary>

        /// 取得一个文本文件的编码方式。如果无法在文件头部找到有效的前导符,encoding.default将被返回。

        /// </summary>

        /// <param name="filename">文件名。</param>

        /// <returns></returns>

        public static encoding getencoding(string filename)

        {

            return getencoding(filename, encoding.default);

        }

        /// <summary>

        /// 取得一个文本文件流的编码方式。

        /// </summary>

        /// <param name="stream">文本文件流。</param>

        /// <returns></returns>

        public static encoding getencoding(filestream stream)

        {

            return getencoding(stream, encoding.default);

        }

        /// <summary>

        /// 取得一个文本文件的编码方式。

        /// </summary>

        /// <param name="filename">文件名。</param>

        /// <param name="defaultencoding">默认编码方式。当该方法无法从文件的头部取得有效的前导符时,将返回该编码方式。</param>

        /// <returns></returns>

        public static encoding getencoding(string filename, encoding defaultencoding)

        {

            filestream fs = new filestream(filename, filemode.open);

            encoding targetencoding = getencoding(fs, defaultencoding);

            fs.close();

            return targetencoding;

        }

        /// <summary>

        /// 取得一个文本文件流的编码方式。

        /// </summary>

        /// <param name="stream">文本文件流。</param>

        /// <param name="defaultencoding">默认编码方式。当该方法无法从文件的头部取得有效的前导符时,将返回该编码方式。</param>

        /// <returns></returns>

        public static encoding getencoding(filestream stream, encoding defaultencoding)

        {

            encoding targetencoding = defaultencoding;

            if(stream != null && stream.length >= 2)

            {

                //保存文件流的前4个字节

                byte byte1 = 0;

                byte byte2 = 0;

                byte byte3 = 0;

                byte byte4 = 0;

                //保存当前seek位置

                long origpos = stream.seek(0, seekorigin.begin);

                stream.seek(0, seekorigin.begin);

               

                int nbyte = stream.readbyte();

                byte1 = convert.tobyte(nbyte);

                byte2 = convert.tobyte(stream.readbyte());

                if(stream.length >= 3)

                {

                    byte3 = convert.tobyte(stream.readbyte());

                }

                if(stream.length >= 4)

                {

                    byte4 = convert.tobyte(stream.readbyte());

                }

                //根据文件流的前4个字节判断encoding

                //unicode {0xff, 0xfe};

                //be-unicode {0xfe, 0xff};

                //utf8 = {0xef, 0xbb, 0xbf};

                if(byte1 == 0xfe && byte2 == 0xff)//unicodebe

                {

                    targetencoding = encoding.bigendianunicode;

                }

                if(byte1 == 0xff && byte2 == 0xfe && byte3 != 0xff)//unicode

                {

                    targetencoding = encoding.unicode;

                }

                if(byte1 == 0xef && byte2 == 0xbb && byte3 == 0xbf)//utf8

                {

                    targetencoding = encoding.utf8;

                }

                //恢复seek位置      

                stream.seek(origpos, seekorigin.begin);

            }

            return targetencoding;

        }

    }

}

       由于在gb2312和utf7编码都没有bom,所以需要指定一个默认的encoding,在找不到合法的bom时,将返回这个encoding。有谁知道如何区分gb2312和utf7编码txt文件的方法,也请告诉我。    由于只是static方法,所以不用new,直接通过类名调用方法,使用起来也很简单。

using system;

using farproc.text;

using system.text;

using system.io;

namespace consoleapplication1

{

    /// <summary>

    /// class1 的摘要说明。

    /// </summary>

    class class1

    {

        /// <summary>

        /// 应用程序的主入口点。

        /// </summary>

        [stathread]

        static void main(string[] args)

        {

            //

            // todo: 在此处添加代码以启动应用程序

            //

            string filename = @"e:/a.txt";

            //生成一个big endian unicode编码格式的文本文件

            streamwriter sw = new streamwriter(filename, false, encoding.bigendianunicode);//你可以试试其他编码,比如encoding.getencoding("gb2312")或utf8

            sw.write("这是一个string");

            sw.close();

   

            //读取

            encoding fileencoding = txtfileencoding.getencoding(filename, encoding.getencoding("gb2312"));//取得这txt文件的编码

            console.writeline("这个文本文件的编码为:" + fileencoding.encodingname);

            streamreader sr = new streamreader(filename, fileencoding);//用该编码创建streamreader

   

            //用下面的方法虽然可以让系统自动判断文本文件的编码格式,但是我们无法取得该文本文件的编码

            //sr.currentencoding永远为 unicode(utf-8)

            //streamreader sr = new streamreader(filename, true);

            //console.writeline("这个文本文件的编码为:" + sr.currentencoding.encodingname);

            console.writeline("这个文本文件的内容为:" + sr.readtoend());

            sr.close();

            console.readline();

        }

    }

}

       .net下的string永远是unicode的,所以只能判断txt文件的encoding。对于byte[],只有自己知道它的encoding才能转换为string 转换为其他编码的byte[],一个例外是把整个txt文件通过stream读入byte[]后也可以根据它的前几个字节判断encoding,对于片断,我们就无能为力了:)

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表