如何对也在Excel中打开的文件执行File.ReadAllLines?

时间:2012-10-05 10:53:44

标签: c# io

如何在没有IO异常的情况下将Excel中打开的文本文件的所有行读入string[]

这个问题可能是答案的一部分,但我不知道如何使用那里的内容: How do I open an already opened file with a .net StreamReader?

3 个答案:

答案 0 :(得分:41)

您的问题是Excel以读/写方式打开文件。当{1}}打开以便在另一个应用程序中写入时,File.ReadAllLines()无法访问该文件。如果您在Excel中以只读方式打开csv,则不会遇到此异常。

这是因为.Net中的实现不会打开具有适当权限的内部流,以便在另一个应用程序具有写入权限时访问该文件。

所以这里的修复很简单,编写自己的ReadAllLines()方法,在启动基础Stream时设置适当的权限。

这个想法很大程度上借鉴了ReadAllLines()本身所做的事情:

public string[] WriteSafeReadAllLines(String path)
{
    using (var csv = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    using (var sr = new StreamReader(csv))
    {
        List<string> file = new List<string>();
        while (!sr.EndOfStream)
        {
            file.Add(sr.ReadLine());
        }

        return file.ToArray();
    }
}

这与ReadAllLines之间的唯一区别是FileShare权限设置为FileShare.ReadWrite,这允许文件即使在打开时具有读/写权限也可以打开在另一个申请中。

现在,您必须了解由此产生的问题,因为可能存在复杂性,因为另一个应用程序对该文件具有写入权限。

  1. 您将要阅读上次保存的文件版本,因此如果您在Excel中有未保存的更改,则此方法将无法读取它们
  2. 如果将此文件保存在Excel中,而此方法正在读取它,则可能会根据具体情况获得异常。这是因为文件在保存时已完全锁定,因此如果您在锁定文件时尝试读取该文件,则会抛出System.IO.IOException
  3. 如果您保存文件并设法避免异常(极不可能,但可能给定特定时间),您将阅读新保存的文件,而不是原始文件。

  4. 要理解为什么在打开其他应用程序写入文件时无法读取该文件,您必须查看.NET中的实际实现。 (这是.Net 4.5中的实现,因此如果您正在查看.Net的差异版本,可能会略有不同。)

    File.ReadAllLines()实际上是这样的:

    public static string[] ReadAllLines(string path)
    {
      if (path == null)
        throw new ArgumentNullException("path");
      if (path.Length == 0)
        throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
      else
        return File.InternalReadAllLines(path, Encoding.UTF8);
    }
    
    
    private static string[] InternalReadAllLines(string path, Encoding encoding)
    {
      List<string> list = new List<string>();
      using (StreamReader streamReader = new StreamReader(path, encoding))
      {
        string str;
        while ((str = streamReader.ReadLine()) != null)
          list.Add(str);
      }
      return list.ToArray();
    }
    

    并查看StreamReader内部正在做什么:

    internal StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool checkHost)
    {
      if (path == null || encoding == null)
        throw new ArgumentNullException(path == null ? "path" : "encoding");
      if (path.Length == 0)
        throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
      if (bufferSize <= 0)
        throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
      this.Init((Stream) new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost), encoding, detectEncodingFromByteOrderMarks, bufferSize, false);
    }
    

    所以我们在这里得出抛出异常的原因,当提供路径时,StreamReader创建FileStream参数设置为FileShare的{​​{1}} 。这意味着它无法与具有该文件的读/写访问权限的其他应用程序共享文件。要覆盖此行为,您需要为Read设置一个Stream的不同设置,这就是我在上面提供的解决方案中所做的。

答案 1 :(得分:1)

试试这个。

FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read,
System.IO.FileShare.ReadWrite)

答案 2 :(得分:1)

只有在读取限制打开文件时才能打开文件。否则,包括ReadAllLines在内的所有方法都可以在不抛出权限异常的情况下工作。