使用fseek进行回溯

5
使用fseek回溯字符fscanf操作可靠吗?
例如,如果我刚才使用fscanf读取了10个字符,但我想要回溯这10个字符,我可以使用fseek(infile, -10, SEEK_CUR)吗?
对于大多数情况,它是有效的,但我似乎在处理字符^M时遇到问题。显然fseek将其视为一个字符,而fscanf则不予以注册,因此在我之前的例子中,包含^M的10个字符块需要使用fseek(infile, -11, SEEK_CUR)fseek(infile, -10, SEEK_CUR)会少1个字符。
为什么会这样呢?
编辑:我在文本模式下使用了fopen
5个回答

8
您看到的是“文本”和“二进制”文件之间的区别。当以文本模式打开文件(在fopen第二个参数中没有'b')时,stdio库可以(确实,必须)根据操作系统的文本文件约定来解释文件内容。例如,在Windows中,一行以\r\n结尾,并且这会被stdio翻译为单个\n,因为那是C的约定。向文本文件写入一个单独的\n会输出为\r\n。
这使得编写处理文本文件的可移植C程序更加容易。然而,有些细节变得复杂,其中之一就是fseeking。由于这个原因,C标准只在文本文件中定义了fseek的几种情况:到达文件开头、到达文件结尾、到达当前位置和到达先前通过ftell检索的位置。换句话说,您不能计算出要在文本文件中寻找的位置。或者您可以,但是您必须自己处理所有特定于平台的细节。
另外,您可以使用二进制文件并自己执行换行转换。同样,可移植性会受到影响。
在您的情况下,如果您只想回到上次执行fscancf的位置,最简单的方法是在执行fscanf之前使用ftell。

谢谢,我不知道有 ftell 这个函数... 绝对比手动使用 fseek 更好的实现方式。 - Yew Long

2
这是因为fseek按字节定位,而fscanf则智能处理回车符和换行符为两个字节,将它们作为一个字符吞噬掉。

1
是的,我认为你是对的;这符合观察结果。我忘记考虑文本和二进制模式,如果我没记错的话,我的fopen默认为文本模式。 - Yew Long
我对使用“智能”一词表示怀疑。在二进制模式下,自己处理\r\n有多难?这样,您就可以在系统之间获得统一的行为(例如,如果您的程序在Unix上运行,但有人向其抛出一个充满\r的DOS文本文件,它仍将正常工作)。我总是选择“考虑文本模式的危害”。 - R.. GitHub STOP HELPING ICE
听起来你是在说,你会自己复制库的内置功能,因为这并不难。按照这个逻辑,为什么要使用任何库呢? - justinhj

1

fseek无法理解文件内容,只是将文件指针向后移动10个字符。

fscanf根据操作系统的不同,可能会以不同方式解释换行符;甚至有可能在DOS上,如果文件中不出现^M,则fscanf会插入^M。请查看随C编译器一起提供的手册。


1

刚在VS2008中尝试了一下,发现fscanf和fseek以相同的方式处理CR和LF字符(作为单个字符)。

所以有两个文件:

0000000: 3132 3334 3554 3738 3930 3132 3334 3536 12345X7890123456

0000000: 3132 3334 350d 0a37 3839 3031 3233 3435 12345..789012345

如果我读取15个字符,则到达第二个'5',然后向后移动10个字符,我的下一个字符读取是第一种情况下的'X'和第二种情况下的CRLF。

这似乎是一个非常特定于操作系统/编译器的问题。


0

你测试了 fscanf 的返回值吗?发一些代码。

看一下 ungetc。你可能需要对其进行循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接