从代码编辑文本或源文件的典型方法是使用 File.ReadAllLines 或 File.ReadAllText 读取文件,进行更改,然后使用 WriteAllLines 或 WriteAllText 将其写出。
但是,如果您要在 Visual Studio 或 Notepad++ 中打开文本文件(比如一些源代码文件),向下滚动几行,进行更改并保存,会处理更多内容。
似乎至少在 Windows 上处理的是一组复杂的规则和启发式方法,至少考虑到:
- 文本文件的推断编码。
- 行尾
- 最后一行是否为“不完整行”(如 diffutils/manual 中所述,即没有行尾字符的行)
我将部分讨论这些只是为了说明复杂性。我的问题是,是否有一套完整的启发式方法、可以使用的已经建立的算法或封装它的现有组件。
推断编码
最常见的源/文本文件:
- 带有 BOM 的 UTF-16
- 带有 BOM 的 UTF-8
- 无 BOM 的 UTF-8
当没有 BOM 时,使用一些启发式方法推断编码。它可能是 ASCII 或 Windows1252 (Encoding.GetEncoding(1252)),或 BOMless UTF-8 这取决于其余数据的样子。如果有一些已知的上 ascii 或可能看起来 UTF-8 的东西。
保存时,需要保持相同的编码。
行尾
您必须保持相同的行尾。因此,如果文件使用 CR/LF,则将其保留在 CR/LF。但是当它只是LF时,请保留它。但它可能会变得更复杂,因为给定的文本文件可能两者都有,并且还需要维护它。例如,一个 CR/LF 的源文件可能在其中有一个只有 LF 行结束的部分。当有人将文本从另一个工具粘贴到文字多行字符串(例如 C# 的 @"" 字符串时,就会发生这种情况。Visual Studio 可以正确处理这个问题。
不完整的行
如果最后一行不完整,则也必须保留。这意味着,如果最后一行不以行尾字符结尾
可能的方法
我认为从一开始就解决所有这些问题的一种方法是将文件视为二进制而不是文本。这意味着无法使用 .NET 中的正常文本文件处理。将需要一组新的 API 来处理编辑此类文件。
我可以对一个组件进行映像,该组件需要您将文件作为内存流打开并将其传递给组件。然后组件可以读取流并向客户端提供面向行的视图,以便客户端代码可以遍历行进行处理。通过迭代的每个元素都将是一个看起来像这样的类型的对象:
class LineElement
{
int originalLineNumber;
string[] lines;
string[] lineEndings;
}
以 Windows 上的普通文本文件为例:
- originalLineNumber 将为 1
- 行将是一个包含文件第一行的一维数组,没有行尾
- lineEndings[0] 将是“\x0D\x0A”
该lines
字段可以修改。可以替换为空数组来删除行,也可以替换为多元素数组来插入行(替换现有行)
lineEndings
数组处理类似。
在许多情况下,不会删除或插入新行,在这种情况下,应用程序代码根本不需要处理行尾。它们只是对 lines[] 数组进行操作,而忽略 lineEndings[] 数组。
我愿意接受其他建议。