fgets返回较少的字符

时间:2015-09-19 02:07:10

标签: c linux fgets

我正在为练习编写汇编程序。汇编程序使用c库函数。我特别关注fgets()函数。 fgets手册页说明:

fgets()  reads in at most one less than size characters from stream and
stores them into the buffer pointed to by s.  Reading  stops  after  an
EOF  or a newline.  If a newline is read, it is stored into the buffer.
A terminating null byte ('\0') is stored after the  last  character  in
the buffer.

我已经声明了一个1024字节的缓冲区,并在fgets功能中使用它来从文件中读取文本。但该程序返回1019个字符。它似乎总是返回5个字符,所以如果我使用1029的缓冲区,它确实会返回1024个字符。我想知道为什么fgets功能以这种方式工作还是我的代码?我的计划如下:

#include <stdio.h>

int main(){

  FILE *fopen(), *fp, *fp2;
  char buff[1024];

  fp = fopen("test.txt", "r");

  fgets(buff, 1024, (FILE*)fp);

  fp2 = fopen("outputtest.txt", "w");
  //fprintf(fp2, "This is testing for fprintf...\n");
  fputs(buff, fp2);

  fclose(fp);
  fclose(fp2);

}

输入在1020位置不包含任何空字节或换行符,因此最多应返回1023。以下是输入:

  

这是一个测试文件。 Linux的发展是其中之一   自由和开源软件协作的突出例子。该   底层源代码可以使用,修改和   根据条款由任何人分发商业或非商业   其各自的许可证,例如GNU通用公共许可证。   通常,Linux以称为Linux发行版的形式打包,   用于桌面和服务器。一些流行的主流Linux   发行版是Debian,Ubuntu,Linux Mint,Fedora,openSUSE,Arch   Linux和Gentoo,以及商业红帽企业Linux   和SUSE Linux Enterprise Server发行版。 Linux发行版   包括Linux内核,支持实用程序和库,以及   通常需要大量的应用软件来实现   分发的预期用途。面向桌面使用的发行版   通常包括X11,Wayland实现或Mir作为   窗口系统,以及附带的桌面环境   GNOME或KDE软件编译;一些发行版也可能   包括资源较少的桌面,如LXDE或Xfce。   打算在服务器上运行的发行版可能会省略所有图形   标准安装的环境,而不是包括其他环境   用于设置和操作LAMP等解决方案堆栈的软件。因为   Linux可以自由再发行,任何人都可以为其创建发行版   任何用途。

输出如下:

  

这是一个测试文件。 Linux的发展是其中之一   自由和开源软件协作的突出例子。该   底层源代码可以使用,修改和   根据条款由任何人分发商业或非商业   其各自的许可证,例如GNU通用公共许可证。   通常,Linux以称为Linux发行版的形式打包,   用于桌面和服务器。一些流行的主流Linux   发行版是Debian,Ubuntu,Linux Mint,Fedora,openSUSE,Arch   Linux和Gentoo,以及商业红帽企业Linux   和SUSE Linux Enterprise Server发行版。 Linux发行版   包括Linux内核,支持实用程序和库,以及   通常需要大量的应用软件来实现   分发的预期用途。面向桌面使用的发行版   通常包括X11,Wayland实现或Mir作为   窗口系统,以及附带的桌面环境   GNOME或KDE软件

以上以一个空格结束,组成完整的1019个字符返回。我想知道造成这种情况的原因。我的汇编程序有效但当然读取的字符数量不正确。有人可以向我解释为什么会这样吗?

提前致谢。

2 个答案:

答案 0 :(得分:2)

我相信其他评论者已经明白了。两个Unicode破折号各有1个字符,但有3个字节。这使你比字符多4个字节。我相信第5个丢失字节是为空终结符腾出空间的一个字符。

我之前的评论询问是否有隐藏的BOM(再见订单标记)已关闭。它似乎是可见的嵌入式Unicode短划线字符,而不是隐藏的嵌入式Unicode BOM。

答案 1 :(得分:2)

将评论转移到答案中。

在Mac OS X上运行,您的代码根据ls -l生成1023字节的输出文件。但是我的输出文件在'KDE软件'(带有空格)之后结束。你是如何在输出上建立文件大小的?你对自己的数量有多确定?问题是否出现在较短的缓冲区大小(比如说32个字节)中 - 也就是说,输出的输出是否比您想象的要短5个字节?

然后rici正确noted

  

示例文本包含两个U + 2014 EM DASH( - )实例,其UTF-8编码为e2 80 94,这当然是相关的。

这很有可能 - 到了确定的程度。它解释了为什么vim在我使用1024|时似乎错误放置了光标 - 它计算字符而不是字节 - 这让我感到困惑。当我在Mac上运行:wc -m时,我得到1019个(多字节)字符,但仍然是1023个字节。

user1803784 observed

  

我使用atom.io文本编辑器来获取计数,错误开始发生在256字节。我尝试了128个字节,64个字节,32个字节,并且没有发生错误,它分别返回127个字节,63个字节,31个字节(如手册页所述“最多只有一个小于字符串的大小字符”)。

由于第一个' - 'em-dash出现在偏移量194处,看起来您的问题与“字节与字符”完全相关,而且您使用的是UTF-8编码数据。作为非零(NUL)字节的纯流处理,您可以将最多1023个字节读入buff,这就是您的代码正在执行的操作。但是,如果计算字符而不是字节,则有两个3字节字符(两个em-dash字符),这意味着字符数比字节数少4。你刚刚知道你的编辑会计算角色;诸如ls之类的程序报告字节。这两个数字通常是不同的。

我们还可以观察到引用的手册页引用的'字符'是char - 类型字符,又称'字节'(在大多数系统上 - 有char不是8的机器位字节)。这种混淆部分源于C标准。

ISO / IEC 9899:2011§7.21.7.2 fgets函数说:

  

fgets函数最多读取的数字少于n指定的字符数   从stream指向的流进入s指向的数组。没有额外的   在换行符(保留)或文件结束后读取字符。一个   在读入数组的最后一个字符后立即写入空字符。

斜体强调添加

相比之下,fgets()的POSIX规范表明fgets()是根据 bytes 指定的:

  

fgets()函数应将 bytes stream读入s指向的数组,直到读取n-1个字节,或者读取<newline>并将其传输到s,否则会遇到文件结束条件。然后该字符串以空字节终止。

斜体强调添加

该页面注释为:

  

此参考页面上描述的功能与ISO C标准一致。此处描述的要求与ISO C标准之间的任何冲突都是无意的。本卷POSIX.1-2008符合ISO C标准。

这是引用ISO / IEC 9899:1999,因为POSIX.1-2008是在C11之前发布的,但C99§7.19.7.2中的措辞与C11中的相同。可以说,POSIX措辞比C标准措辞更容易理解。但是,标准的定义部分说:

  

3.7
  1 字符
  用于组织,控制或的一组元素的成员   数据表示

     

3.7.1   1 字符   单字节字符
  适合字节的位表示

     

3.7.2
  1 多字节字符   表示扩展字符集成员的一个或多个字节的序列   源或执行环境
  2注意扩展字符集是基本字符集的超集。

     

3.7.3
  1 宽字符   值可由wchar_t类型的对象表示,能够表示任何字符   在当前的区域设置

因此,在上下文中,“字符”表示大多数人认为的“字节”(有警告 - 并非所有机器都有CHAR_BIT == 8)。