【问题标题】:How can I detect if a file has Unix line feeds (\n) or Windows line feeds (\r\n)?如何检测文件是否有 Unix 换行符 (\n) 或 Windows 换行符 (\r\n)?
【发布时间】:2012-08-03 11:59:19
【问题描述】:

我正在通过 FileStream 更改文件(这是一个非常大的文件,我只需要更改标题而不重写整个文件。

该文件可以有 Unix 或 Windows 换行符,对我来说知道哪一个很重要,这样我可以在更新文件时将正确的换行符写回文件中。

我可以编写一个简单的函数来使用 FileStream 以块的形式读取文件并检查换行符。

但是这个问题之前肯定已经解决了,如果不是在 C# 中,那么在 Win32 API 中?

检测文件换行样式最有效的方法是什么?

【问题讨论】:

  • 不确定,因此有评论,但是否可以使用正则表达式,例如:\r\n$?这将检查换行符是否以\r\n 结尾。如果不是,那么它必须是 Unix 行。
  • 不太可能,因为在 .Net 框架文件访问对象上使用任何“ReadLine”方法都会去除换行符。如果您不关心文件使用什么换行符样式,它们会很好。如果我将文件作为流读取,我可以执行您建议的操作(这基本上相当于我可能采用的方法..)
  • 你能保证文件中的所有行结尾都是一致的吗?从技术上讲,在同一个文件中可以有不同的混合行结尾。
  • 如果您只是阅读我建议使用StreamReader.ReadLine() 方法的数据。 MSDN 因为它可以处理行尾的所有变体。但这并不能解决将相同的结尾写回文件的问题。 :-(
  • 我很高兴只阅读前一两行来确定换行符。我正在替换文件上的标题,所以只要我得到第一个正确,我就不会损坏文件...

标签: c# windows file-io newline


【解决方案1】:

不幸的是,我认为没有办法 100% 确定它是 Unix 还是 DOS 文件,因为大多数编辑器在打开/保存时不会更正具有“错误”结尾的文件。

我会将文件作为流读取并搜索出现的“\r\n”并且仅搜索“\n”

对搜索结果使用简单的统计分析(即哪一个具有最高的命中数)可能会为您提供正确的答案。如果文件很大,那么读取文件的前 X% 就足够了。

一个更简单的解决方案当然是只搜索“\r\n”,如果找到,就假设它是一个 DOS 文件。如果文件是机器生成的,这应该可以 100% 工作。

至于 .NET Framework/WinAPI 中的任何现有代码,我还没有看到任何执行此操作的代码。

【讨论】:

    【解决方案2】:

    正如 Per 所说,如果不打开文本文件并通过字节流式传输,确实无法确定文本文件的内容。如果您使用 http 下载文件,您可能会很幸运,您可以获得一个指示文件类型的 mime 类型,但通常它只是“八位字节流”。

    虽然您可以强制使用它,并阅读直到找到换行符(“\n”)然后备份一个字符并查看是否有回车符(“\r”),但我会采取更统计的方法因为您必须以任何方式读取数据。

    1) 选择要读取的字节样本大小,这应该可以从文件中获得至少 2 或 3 条记录。

    2) 将遇到的每个字节(i'massumign 此处设置的单字节字符)存储为直方图。您可以通过将计数存储在由字节值索引的数组中来做到这一点,或者您可以使用字典。

    3) 查看回车和换行值的计数。如果您有换行计数并且没有回车,那么它是一个 unix 文件。如果 carraige return 和 line feed 计数,那么它是一个 windows 文件。

    这种方法还允许您对入站文件进行质量检查。您的直方图中是否有不是 aplha 数字的字符?然后有人传给你一个二进制文件。期望全部大写?然后查找大写字符之外的计数。您可以进行多项检查来避免处理非文本文件。

    【讨论】:

    • 您和@Per 的解决方案都假定每个文件的所有行尾都是一致的。在野外,技术上很可能有混合的行尾标记。
    • 是的,但这里的练习是确定文件是 unix 还是 windows。由于问题中的说明,我假设入站文件采用一种或另一种格式。如果一个预期混合的 LF 和 CR/LF 终止记录,那么标题行以哪一个终止可能无关紧要。
    【解决方案3】:

    感谢大家的建议。我很惊讶没有找到容易重用的东西,所以我创建了一个简单的函数,我在这里包含。请注意,它只找到第一个换行符(\n 或 \r\n)并将其作为匹配项返回。足以满足我的需求,但可能不够强大。

        public bool TryDetectNewLine(string path, out string newLine)
        {
            using (var fileStream = File.OpenRead(path))
            {
                char prevChar = '\0';
    
                // Read the first 4000 characters to try and find a newline
                for (int i = 0; i < 4000; i++)
                {
                    int b;
                    if ((b = fileStream.ReadByte()) == -1) break;
    
                    char curChar = (char)b;
    
                    if (curChar == '\n')
                    {
                        newLine = prevChar == '\r' ? "\r\n" : "\n";
                        return true;
                    }
    
                    prevChar = curChar;
                }
    
                // Returning false means could not determine linefeed convention
                newLine = Environment.NewLine;
                return false;
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2013-12-02
      • 2011-08-22
      • 2010-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-23
      • 1970-01-01
      相关资源
      最近更新 更多