使用sprintf()/write()的C程序有额外的字符

使用sprintf()/write()的C程序有额外的字符,c,printf,fwrite,C,Printf,Fwrite,我正在编写一个C程序,其中用户将10个整数输入到一个数组中,该程序在控制台上输出该数组的倒数,并将其写入一个文件。我的程序唯一的问题是,在两个输出中都有额外的字符。在控制台中,第一个整数前面有一个“%”。在我写入的文件中,每个整数前面都有两个空框,第一个除外(最后一个整数后面还有一行) #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #inc

我正在编写一个C程序,其中用户将10个整数输入到一个数组中,该程序在控制台上输出该数组的倒数,并将其写入一个文件。我的程序唯一的问题是,在两个输出中都有额外的字符。在控制台中,第一个整数前面有一个“%”。在我写入的文件中,每个整数前面都有两个空框,第一个除外(最后一个整数后面还有一行)

#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[]) {
  int i;
  int input;
  char *filename = "/home/privateinfo/5/output";
  char *buffer = malloc(sizeof(int));
  int *tenInts = malloc(10 * sizeof(int));

  /* open the file for writing */
  int fd = open(filename, O_WRONLY | O_TRUNC);

  if (fd < 0) {
    return -1;
  }

  /* read ten integers from keyboard and store in array*/
  write(1, "Enter 10 integers:\n", 20) ;

  for (i = 0; i < 10; i++) {
    scanf("%d", &input);
    tenInts[i] = input;
  }

  write(1, "\nThe integers reversed are:\n", 30);

  /* print array in reverse and write to file*/
  for (i = 9; i > -1; i--) {
    sprintf(buffer, "%d\n", tenInts[i]);
    write(1, buffer, sizeof(int));
    if(write(fd, buffer, sizeof(int)+1) < 0) {
      write(2, "There was an error writing to the file.\n", 50);
      return -1;
    }
  }

  /* close the file and clear pointer */
  close(fd);

  return 0;
}
#包括
#包括
#包括
#包括
#包括
int main(int argc,char const*argv[]{
int i;
int输入;
char*filename=“/home/privateinfo/5/output”;
char*buffer=malloc(sizeof(int));
int*tenInts=malloc(10*sizeof(int));
/*打开文件进行写入*/
int fd=打开(文件名,O_WRONLY | O_TRUNC);
如果(fd<0){
返回-1;
}
/*从键盘读取十个整数并存储在数组中*/
写入(1,“输入10个整数:\n”,20);
对于(i=0;i<10;i++){
scanf(“%d”,输入(&I));
tenInts[i]=输入;
}
写入(1,“\n反转的整数为:\n”,30);
/*反向打印数组并写入文件*/
对于(i=9;i>-1;i--){
sprintf(缓冲区,“%d\n”,tenInts[i]);
写入(1,缓冲区,sizeof(int));
if(写入(fd,缓冲区,sizeof(int)+1)<0){
写入(2,“写入文件时出错。\n”,50);
返回-1;
}
}
/*关闭文件并清除指针*/
关闭(fd);
返回0;
}

底线是,您没有为
缓冲区
分配足够的存储空间,并且当您试图在
缓冲区
中存储的字符超过您的空间时,很可能会调用未定义的行为。此外,当您试图
标准输出
写入更多字符时,也会调用未定义的行为您在字符串文字的字符计数中有一个(或多个)字符,因为
“\n”
是一个字符(换行符、ASCII
10
0xa
),而不是
'\'
'n'
的多个字符

不幸的是,这只是冰山一角。首先,请注意,避免在代码中硬编码幻数(
10
是一个幻数),并避免硬编码文件名)。如果因为需要
11
整数而需要更改像
10
这样的数字,请查看需要进行多少更改。如果要写入其他文件,则必须重新编译代码。请改为:

#define NINT     10   /* if you need a constant, #define one (or more) */
#define INTCHARS 32   /* adequate for LLONG_MIN or ULLONG_MAX (20+1 chars) */
#define FCRMODE  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
现在,如果需要进行更改,您可以在代码顶部的单个位置更改常量

对于文件名,您可以提供一个默认值,但当声明为时,您的程序接受
main
的参数:

int main (int argc, char **argv) {
将文件名作为参数传递给您的程序,或使用硬编码文件名作为默认值。示例:

    char *filename = argc > 1 ? argv[1] : "/home/privateinfo/5/output";
如果提供了参数,则将其用作文件名,否则将使用默认值

在您的分配中:

char *buffer = malloc(sizeof(int));
您只能将4个字节分配给
缓冲区
,这意味着您最多只能存储3位数字和nul终止字符,或者由于您还包括
'\n'
字符,您最多只能存储2位数字。相反,没有理由分配缓冲区。最长的
长整型
无符号64位计算机上的long-long
是20个字符(包括
'-'
减号)
+1
作为nul终止字符,总共需要21个字符。只需定义一个32字节的缓冲区,您就可以将
'\n'
包含10个安全字符,例如

    char buffer[INTCHARS] = "";
接下来,不要为
write
计算字符数。如果您打印字符串文字,只需使用
strlen()
即可获得适当数量的字符以进行输出。或者,如果您需要创建一个字符串以使用
sprintf
进行写入,保存返回值它会告诉您写入缓冲区的字符数(不包括nul终止字符),例如

(当您可以将字符串直接转换为
stdout
时,为什么要使用
write
是一个谜。)

不要将负值返回到shell。POSIX要求只返回正值。这就是宏
EXIT\u FAILURE
被定义为
1
的原因

其余错误只是上述内容的附加合并。总而言之,您可以:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define NINT     10   /* if you need a constant, #define one (or more) */
#define INTCHARS 32   /* adequate for LLONG_MIN or ULLONG_MAX (20+1 chars) */
#define FCRMODE  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH

int main (int argc, char **argv) {

    int fd, i, input, nchar, *tenInts;
    char *filename = argc > 1 ? argv[1] : "/home/privateinfo/5/output";
    char buffer[INTCHARS] = "";

    if (!(tenInts = malloc (NINT * sizeof *tenInts))) {
        perror ("malloc-tenInts");
        return 1;
    }

    /* open the file for writing */
    if ((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, FCRMODE)) < 0) {
        perror ("fopen-filename");
        return 1;
    }

    /* read NINT integers from keyboard and store in array*/
    nchar = sprintf (buffer, "Enter %d integers:\n", NINT);
    write (STDOUT_FILENO, buffer, nchar);       /* (or simply use printf) */

    for (i = 0; i < NINT; i++) {
        if (scanf ("%d", &input) != 1) {
            fputs ("error: invalid integer input.\n", stderr);
            return 1;
        }
        tenInts[i] = input;
    }

    write (STDOUT_FILENO, "\nThe integers reversed are:\n",
            strlen ("\nThe integers reversed are:\n"));

    /* print array in reverse and write to file*/
    for (i = NINT-1; i >= 0; i--) {
        nchar = sprintf (buffer, "%d\n", tenInts[i]);
        write (STDOUT_FILENO, buffer, nchar);
        if (write (fd, buffer, nchar) < 0) {
            write (STDERR_FILENO, "There was an error writing to the file.\n",
                    strlen ("There was an error writing to the file.\n"));
            return 1;
        }
    }

    /* ALWAYS validate close of file (after a write) */
    if (close (fd) == -1)
        perror ("close-fd");

    return 0;
}
输出文件

$ cat dat/tenintwrite.txt
100
90
80
70
60
50
40
30
20
10

仔细检查一下,如果您有进一步的问题,请告诉我。

缓冲区的长度与
大小(int)
之间没有相关性。您不能
一个
int
写入
标准输出
(例如,使用
写入(1,缓冲区,大小(int));
--需要多少字节
“4312981”
),您可以
fputs(buffer,stdout);
(您可以简单地
put(buffer)
,但是您在
buffer
中包含了
'\n'
(出于某种奇怪的原因)。不要
将-1;
返回到shell。
返回1;
以指示错误。您无法正确使用任何输入函数(如
scanf(“%d”,&input);
)除非您检查返回。
“输入10个整数:\n”
仅为
19
个字符,而不是
20
“\n”
是单个字符(ASCII 10,0xa)。我将写调用改为使用strlen(缓冲区),而不是以前的操作。我还删除了“\n”从缓冲区。这两个修复为我解决了它,谢谢。我是一个有点新的,所以idk如果有一种方法来标记一个评论作为一个正确的答案。
$ ./bin/tenintwrite dat/tenintwrite.txt
Enter 10 integers:
10 20 30 40 50 60 70 80 90 100

The integers reversed are:
100
90
80
70
60
50
40
30
20
10
$ cat dat/tenintwrite.txt
100
90
80
70
60
50
40
30
20
10