这么多年代码发展, 竟然发现读到一个文件位置竟不容易
有一个 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 才会有效