使用TextFieldParser记录CSV文件行字节偏移量的问题
关于TextFieldParser记录CSV行字节偏移量的问题
首先明确告诉你:是可行的,但你遇到的FileStream.Position不更新的问题,根源在于TextFieldParser的内部缓冲机制。
为什么FileStream.Position会一直不变?
TextFieldParser为了提高读取效率,会一次性从文件流中读取一大块数据到内存缓冲区(你看到的5120就是第一次读取的缓冲大小),之后的逐行解析都是从这个缓冲区里取数据,而不是直接操作文件流。只有当缓冲区里的数据被耗尽时,它才会再次从文件流中读取下一块数据,此时FileStream.Position才会跳转到新的位置——这就是为什么你到第62行才看到Position变化的原因。
所以直接用FileStream.Position是拿不到当前行的实际字节偏移的,它反映的是缓冲区已经读取到的流位置,不是当前解析行的位置。
怎么正确记录每行的字节偏移量?
既然TextFieldParser没有直接提供行的字节偏移接口,我们可以自己手动跟踪,结合你使用的编码(这里是1251,单字节编码,处理起来相对简单),具体思路如下:
- 初始化一个偏移量变量,从0开始(对应文件开头)。
- 每次读取一行后,计算该行字符串加上换行符的字节数(注意Windows系统换行符是
\r\n,占2字节;Linux是\n占1字节,根据你的文件实际情况调整)。 - 累加这个字节数到偏移量变量,就能得到下一行开头的字节位置。
示例修改代码
Dim encoding As System.Text.Encoding = System.Text.Encoding.GetEncoding(1251) Dim currentOffset As Long = 0 ' 初始偏移为文件开头 Using FStream As FileStream = File.Open("p.csv", FileMode.Open) Using csvReader As New Microsoft.VisualBasic.FileIO.TextFieldParser(FStream, encoding) csvReader.TextFieldType = FileIO.FieldType.Delimited csvReader.SetDelimiters(";") csvReader.HasFieldsEnclosedInQuotes = False csvReader.TrimWhiteSpace = True While csvReader.LineNumber < 100 Try Dim lineContent As String = csvReader.ReadLine() If lineContent Is Nothing Then Exit While ' 防止读到文件末尾 Console.WriteLine(lineContent) ' 计算当前行的字节数(包含换行符) Dim lineBytes As Byte() = encoding.GetBytes(lineContent & vbCrLf) Console.WriteLine("Line number {0}. Current line start offset: {1}", csvReader.LineNumber, currentOffset) ' 更新偏移量到下一行开头 currentOffset += lineBytes.Length Catch ex As Exception ' 这里可以添加异常处理逻辑 Console.WriteLine($"Error reading line {csvReader.LineNumber}: {ex.Message}") End Try End While End Using End Using
额外注意点
- 如果你的CSV文件里有换行符被包含在引号字段中的情况(虽然你设置了
HasFieldsEnclosedInQuotes = False,但如果后续有变化),上面的方法会出错,因为此时行内的换行符不属于行分隔符。这种情况下,你可能需要更复杂的逻辑,或者考虑使用第三方CSV库,这类库通常会提供行偏移的相关属性。 - 编码的影响:如果使用的是UTF-8这类可变字节编码,
encoding.GetBytes()的结果会更准确,因为它会正确计算每个字符的字节数。
内容的提问来源于stack exchange,提问作者ALT user
相关产品推荐
相关产品推荐

