Программа C, использующая sprintf () / write (), имеет дополнительные символы - PullRequest
0 голосов
/ 01 февраля 2020

Я пишу 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;
}

Вот мой вывод.

1 Ответ

1 голос
/ 01 февраля 2020

Суть в том, что вы не можете выделить достаточно места для buffer и, скорее всего, вызовете Неопределенное поведение , когда вы попытаетесь сохранить больше символов, чем у вас есть места в buffer. Вы дополнительно вызываете Неопределенное поведение , когда вы пытаетесь на write больше символов до stdout, чем у вас есть символы для (у вас на счету не по одному (или больше) символов в строковых литералах, поскольку "\n" - это один символ ( символ новой строки , ASCII 10 или 0xa), а не несколько символов '\' и 'n'.

К сожалению, это только верхушка айсберга. Во-первых, как пара замечаний, избегайте жесткого кодирования Magi c -Numbers в вашем коде (10 является Magi c -Номер ) и избегать жесткого кодирования имен файлов). Если вам нужно изменить число, например 10, потому что вы хотите 11 целых чисел, посмотрите, сколько изменений необходимо. Если вы хотите записать в другой файл, вы должны пересобрать свой код. Вместо этого:

#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 байта для buffer, что означает, что вы можете хранить максимум 3 цифры и символ с нулевым окончанием , или, так как вы также включаете символ '\n', вы больше не можете хранить чем 2-ди git число. Вместо этого нет причин выделять для буфера. Самый длинный long long int или unsigned long long на 64-разрядном компьютере составляет 20 символов (включая знак '-' минус) +1 для завершающего нуль символа , всего 21 символ необходимо. Просто определите 32-байтовый буфер, и вы можете включить свой '\n' с 10 символами безопасности, например,

    char buffer[INTCHARS] = "";

Далее, не считайте символы для write. Если вы печатаете String-Literals , простое использование strlen() даст вам правильное количество символов для вывода. Или, если вам нужно создать строку для записи с sprintf, , сохраните возвращаемую , она сообщит вам, сколько символов было записано в буфер (не включая nul определяющий символ), например

    /* 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) */

(почему вы используете write - загадка, когда вы можете просто printf строку с преобразованием непосредственно в stdout)

Не возвращать отрицательные значения в оболочку. POSIX требует, чтобы возвращались только положительные значения. Вот почему макрос EXIT_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;
}

Теперь у вас не будет выводиться забавных блоков (хороший признак Неопределенное поведение ) при запуске вашего кода.

Пример использования / Вывод

$ ./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

Просмотрите все и дайте мне знать, если у вас есть дальнейшие вопросы.

...