TextReader / StreamReader中是否有“go to line”选项?

我有一个25k行的巨大文本文件。在文本文件中,每行以“1 \ t(linenumber)”开头

例:

1 1 ITEM_ETC_GOLD_01 골드(소) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_small.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 1 2 ITEM_ETC_GOLD_02 골드(중) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_normal.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 1 3 ITEM_ETC_GOLD_03 골드(대) xxx xxx xxx_TT_DESC 0 0 3 3 5 0 180000 3 0 1 0 0 255 1 1 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_money_large.bsr xxx xxx xxx 0 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 10000 표현할 골드의 양(param1이상) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 1 4 ITEM_ETC_HP_POTION_01 HP 회복 약초 xxx SN_ITEM_ETC_HP_POTION_01 SN_ITEM_ETC_HP_POTION_01_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 60 0 0 0 1 21 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_01.ddj xxx xxx 50 2 0 0 1 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 120 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 1 5 ITEM_ETC_HP_POTION_02 HP 회복약 (소) xxx SN_ITEM_ETC_HP_POTION_02 SN_ITEM_ETC_HP_POTION_02_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 110 0 0 0 2 39 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_02.ddj xxx xxx 50 2 0 0 2 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 220 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 1 6 ITEM_ETC_HP_POTION_03 HP 회복약 (중) xxx SN_ITEM_ETC_HP_POTION_03 SN_ITEM_ETC_HP_POTION_03_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 200 0 0 0 4 70 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_03.ddj xxx xxx 50 2 0 0 3 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 370 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 1 7 ITEM_ETC_HP_POTION_04 HP 회복약 (대) xxx SN_ITEM_ETC_HP_POTION_04 SN_ITEM_ETC_HP_POTION_04_TT_DESC 0 0 3 3 1 1 180000 3 0 1 1 1 255 3 1 0 0 1 0 400 0 0 0 7 140 -1 0 -1 0 -1 0 -1 0 -1 0 0 0 0 0 0 0 100 0 0 0 xxx item\etc\drop_ch_bag.bsr item\etc\hp_potion_04.ddj xxx xxx 50 2 0 0 4 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 0 0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 570 HP회복양 0 HP회복양(%) 0 MP회복양 0 MP회복양(%) -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx -1 xxx 0 0 

问题:如何直接阅读,例如第5行?

您可以使用我的LineReader类( MiscUtil中的一个或此处的简单版本 )来实现IEnumerable ,然后使用LINQ:

 string line5 = new LineReader(file).Skip(4).First(); 

无可否认,这假定为.NET 3.5。 否则,打开TextReader (例如使用File.OpenText )并只调用ReadLine()四次以跳过您不想要的行,然后再读取第五行。

除非你确切地知道每行中有多少字节,否则无法“快捷”。

如果您正在处理固定宽度的数据格式(即,您知道所有行的长度相同),您可以将长度乘以所需的行号,并使用Stream.Seek查找第n行的起点。

如果线条不是固定长度,则需要找到正确数量的换行符,直到您处于所需行的开头。 使用StreamReader.ReadLine最简单。 (您可以使用扩展方法将文件设置为IEnumerable ,正如Jon Skeet建议的那样 – 这会让您获得更好的语法,但是您将使用ReadLine。

如果性能是一个问题,那么使用Stream.Read方法手动扫描文件中的 字节序列可能会(稍微)一些。 我没有测试过; 但是StreamReader显然需要做一些工作来构造字节序列中的字符串 – 如果你不关心第一行,这项工作可以保存,所以理论上你应该能够制作一个性能更好的扫描方法。 但是,这对你来说会更多。

您不能直接跳转到文本文件中的一行,除非每行都是固定宽度并且您使用的是固定宽度编码(即不是UTF-8 – 这是现在最常见的一种)。

唯一的方法是读取行并丢弃你不想要的行。

或者,您可以在文件的顶部(或在外部文件中)放置一个索引(例如),第1000行从字节偏移[x]开始,第2000行从字节偏移[y]开始等。然后在FileStream上使用.Seek().Seek()移动到最近的索引点,然后向前走。

假设最简单的方法(没有索引),Jon的例子中的代码应该可以正常工作。 如果你不想要LINQ,你可以在.NET 2.0 + C#2.0中找到类似的东西:

 // to read multiple lines in a block public static IEnumerable ReadLines( string path, int lineIndex, int count) { if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path"); if (lineIndex < 0) throw new ArgumentOutOfRangeException("lineIndex"); if (count < 0) throw new ArgumentOutOfRangeException("count"); using (StreamReader reader = File.OpenText(path)) { string line; while (count > 0 && (line = reader.ReadLine()) != null) { if (lineIndex > 0) { lineIndex--; // skip continue; } count--; yield return line; } } } // to read a single line public static string ReadLine(string path, int lineIndex) { foreach (string line in ReadLines(path, lineIndex, 1)) { return line; } throw new IndexOutOfRangeException(); } 

如果你需要测试线的值(而不仅仅是线索引),那么这也很容易; 只需调整迭代器块。

如果你要从文件中查找很多不同的行(但不是全部),那么你可以通过构建索引获得一些好处。 使用已经存在的任何建议,但是当您为已经找到的任何行构建一个字节偏移数组时,您可以避免每次从头重新扫描文件。

附录:
如果您只需要偶尔的“随机”线,还有一种方法可以快速完成,但代价是更复杂的搜索(如果Jon的答案足够快,我为了简单起见肯定会坚持这一点)。

您可以进行“二分搜索”,只需开始查看序列“1”的文件中途,您找到的第一个匹配项就会让您知道您找到的行号; 然后根据您要查找的行相对于找到的数字,您将继续递归拆分。

为了获得额外的性能,您还可以假设线条长度大致相同,并且算法“猜测”您要查找的线的大致位置相对于文件中的总线数,然后执行此搜索在那里。 如果您不想对文件的长度做出假设,您甚至可以通过先分成两半来使其成为自我素数,并使用它首先找到的行号作为文件中有多少行的近似值。整个。

绝对不容易实现,但如果你在拥有大量行的文件中有很多随机访问权限,它可能会带来性能提升。

如果你需要能够使用在后台执行ReadLine()的函数跳转到第24,000行,那将会有点慢。

如果行号很高,您可能想要对文件中的行位置进行某种有根据的猜测并从那里开始读取。 这种方式到达24,567行,你不必先读取24,566行。 您可以跳到中间的某个位置,根据/ t之后的数字找出您所在的行,然后从那里开始计数。

前段时间我和一个必须在RDBMS之前建立数据库的开发人员共同工作。 他对你的问题的解决方案类似于我刚刚写的内容,但在他的情况下,他将地图保存在一个单独的文件中。 地图可以将每百行映射到其在文档中的位置。 像这样的地图可以很快加载,这可能会增加读取时间。 当时他的系统对于只读数据非常快速有效,但对读/写数据不是很好。 (每次更改线条时都需要更改整个地图,这不是很有效)