这么多年代码发展, 竟然发现读到一个文件位置竟不容易

有一个 1G 大的 文本 行文件, 首先我要计出特定行在文件中位置,保存到一个偏移量表文件, 便于后面任意时候打开时可定位取文件块;

 

以为是一个发展了 30多年的代码早就解决的基本问题(当年在文件流上做过各种定位、分块读),看看老外(很多是10年前写的)这么多的讨论,要解决这个问题, 到现在竟然没有发展出一个基本应用的类来解决 :

https://www.daniweb.com/programming/software-development/threads/35078/streamreader-and-position

http://stackoverflow.com/questions/5404267/streamreader-and-seeking

在 StreamReader.ReadLine 下 StreamReader.Position 和 FileStream.Position 都是读入 1024 大小的 byte, 读入指针如下变化

也就是说, 你根本不可能通过 当前文件位置 FileStream.Position  获得真正的当前文件位置指针, 而且没有任何成熟办法支持你获得这个位置

以上打印结果由如下代码实现:

strLine = sr.ReadLine ();

Debug.Log ("offset = " + sr.BaseStream.Position + " | " + strLine + " | " + strLine.Length);

在网上也看到同样问题 :  

使用stream.Seek可以正确定位读取文件某位置上的数据吗..._CSDN论坛 最后也没有答案, 不知道这位兄弟是怎么解决的。

 先解决第一问题, 如何可以在文件里定位:

FileStream 打开文件, 通过 Seek 定位后, 要在 StreamRead 里起到定位作用

在看了一堆文档和做了一堆试验, 确认在流里 seek 有效方法 : 

无论你是 

FileStream.Seek( 10, SeekOrigin.Begin);

还是 

StreamReader.BaseStream.Seek (4, SeekOrigin.Begin);

 

都要重新获取一个流, 才能得到当前文件的正确位置:
sr = new StreamReader (aFile);

也就是说 seek 实质上发生在 FileStream 中, 要用一个新的 Stream 来重新对应, 进行存取;

 

第二个问题, 如何在读入字串后, 可以得到读出后的真正文件指针位置

如果你用 StreamReader.ReadLine,  如今看是没有 基础方法 的得到真实位置

我的是文本文件,我采用的方法是逐行累加一下, 自己记录一个长度, 第一次没记入回车, 位置得到的不对,

回车的处理处理有一个问题,就是系统是如何处理的, 在老外的文章里有一个办法

 如果你是用 ReadLine , 想得到读出长度要加回车长度, 而回车的长度是不定的,

 

这里  c# - StreamReader and seeking - Stack Overflow  最后一个回答里,给出一个得到回车长度的方法:

int newLineBytes = System.Environment.NewLine.Length;

经试验不一定对, 我的回车 “x0A"  而这个返回是2个字节;

 

暂时我只好是 readline  + 1 来得到我自己文件的长度了。 

 

看样子, 还是用 readbyte 来自己写是最终方法

重新设计代码方案: 

关于FileStream读取大文件问题 - YoMe - 博客园 

 我自己写的记录偏移量的代码如下, 个人可根据不同文件格式来改变位置标志:

 

 

    //
    void oyRecordOffset()
    {
        using (FileStream fsRead = new FileStream(xPath + "/1014test1.txt",FileMode.Open)) {            // oyOffset.txt   1014test1.txt
            byte[] arr = new byte[2000];
            //记录到底读取了多少字节的数据
            int count = 0;
            bool beol = false;

            long lOffset = 0;
            int iLines = 0;


            while (fsRead.Position < fsRead.Length) {
                //每一次读取,。返回真正读取到的字节数,用count记录(最后一次读取后可能count可能会小于200)
                count = fsRead.Read (arr, 0, arr.Length);
                //Debug.Log ("count ===> " + count);

                for (int i = 0; i < arr.Length; i++, lOffset++) {
                    if (arr [i] == '\x0a') {    
                        //一个行尾
                        beol = true;
                    } else {
                        if (arr [i] == '\x0d') {
                            // 一个行尾
                            beol = true;

                        } else {
                            if (beol)
                            {
                                //Debug.Log (" * line offset => " + lOffset);
                                iLines++;
                                beol = false;
                            } 
                                
                        }
                    }

                    if (iLines >= 8000) {
                        arOffset [iFrames] = lOffset; 
                        iFrames++;
                    Debug.Log (" * => iFrames " + iFrames + " | " + lOffset);
                        iLines = 0;
                    }
                }

//                if(iFrames > 5)
//                    break;
//            string s = Encoding.ASCII.GetString(arr);
//                Debug.Log (" => len "+ s.Length + " | " + s);
            }
            Debug.Log (" => iFrames " + iFrames);
        }
    }

 

 

 


另外:对如何正确使用 FileStream.Seek 我进行的测试

 

FileStream aFile = new FileStream (path + "/oyOffset.txt",FileMode.Open,FileAccess.ReadWrite);

StreamReader sr = new StreamReader (aFile);

.... 

aFile.Seek( 10, SeekOrigin.Begin);
sr = new StreamReader (aFile);  重新定位后, 要重新打开 stream 才会有效

 

 

参考 :关于C#中的StreamReader与FileStream这两个类

posted @ 2016-12-03 00:31  ouyang80  阅读(185)  评论(0编辑  收藏  举报