take (0) on File.ReadLines似乎没有处理/关闭文件句柄
本文关键字:处理 文件句柄 on ReadLines File take | 更新日期: 2023-09-27 18:04:00
我有一个函数,它跳过n
行代码,并使用File.ReadLines
与Skip
和Take
组合从给定文件中获取y
行。当我试图打开filePath
给出的文件下次:
string[] Lines = File.ReadLines(filePath).Skip(0).Take(0).ToArray();
using (StreamWriter streamWriter = new StreamWriter(filePath))
{
// ...
}
我在"using
"行上得到File in use by another process
异常。
看起来IEnumerable.Take(0)
是罪魁祸首,因为它返回一个空的IEnumerable
而没有枚举File.ReadLines()
返回的对象,我认为CC_11没有处理文件。
我说的对吗?他们不应该列举来避免这种错误吗?如何正确地做到这一点?
这基本上是File.ReadLines
的错误,而不是Take
。ReadLines
返回IEnumerable<T>
,这在逻辑上应该是惰性的,但是它急切地打开文件。除非对返回值进行迭代,否则没有什么可处理的。
也是只迭代一次。例如,您应该能够这样写:
var lines = File.ReadLines("text.txt");
var query = from line1 in lines
from line2 in lines
select line1 + line2;
…这样就得到了文件中直线的外积。它不能,因为它破碎了。
File.ReadLines
应该这样实现:
public static IEnumerable<string> ReadLines(string filename)
{
return ReadLines(() => File.OpenText(filename));
}
private static IEnumerable<string> ReadLines(Func<TextReader> readerProvider)
{
using (var reader = readerProvider())
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
不幸的是,它不是:(
选项:
- 用上面的代替
File.ReadLines
编写自己的
Take
实现,使总是开始迭代,例如public static IEnumerable<T> Take<T>(this IEnumerable<T> source, int count) { // TODO: Argument validation using (var iterator = source.GetEnumerator()) { while (count > 0 && iterator.MoveNext()) { count--; yield return iterator.Current; } } }
从参考源代码File.ReadLines()
上面的注释来看,很明显负责的团队知道关于这个"bug":
无法更改以保持与4.0兼容的已知问题:
- 底层
StreamReader
被预先分配给之前的IEnumerable<T>
甚至还调用了GetEnumerator
。虽然这在例外情况下是好的,如直接抛出DirectoryNotFoundException
和FileNotFoundException
File.ReadLines
(这可能是用户期望的),它也意味着阅读器如果用户从未在可枚举对象(因此.
所以他们希望File.ReadLines()
在传递无效或不可读的路径时立即抛出,而不是在枚举时抛出。
替代方法很简单:不调用Take(0)
,或者如果对文件的内容不感兴趣,则不读取整个文件。
在我看来,根本原因是如果count
为零,Enumerable.Take
迭代器不会处理底层迭代器,因为代码没有进入foreach
循环-请参阅参考资料。如果按以下方式修改代码,问题就解决了:
static IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count)
{
foreach (TSource element in source)
{
if (--count < 0) break;
yield return element;
}
}