Di C, bagaimana saya bisa menghasilkan kesalahan jika string input terlalu besar?

Saya ingin membaca daftar kata dari sebuah file, yang memiliki satu kata per baris. Masing-masing kata harus maksimal 4 karakter. Bagaimana saya bisa menghasilkan kesalahan jika salah satu barisnya lebih panjang dari itu?

Saya mencoba membaca kata-katanya menggunakan fgets

char buf[5];
fgets(buf, 5, stdin);

dan dengan scanf

char buf[5];
scanf("%4s", &buf);

namun dalam kedua kasus ini membagi garis panjang menjadi garis yang lebih kecil. Misalnya qwerasdf dibaca sebagai dua kata, qwer dan asdf. Apakah ada cara untuk mendeteksi bahwa ia mencoba membaca baris panjang dengan lebih dari 4 karakter dan malah memberikan kesalahan?

Satu-satunya alternatif yang dapat saya pikirkan adalah membaca masukan karakter demi karakter dan mengurus semuanya sendiri. Tetapi apakah ada solusi yang lebih sederhana dengan menggunakan fungsi dari perpustakaan standar?


person hugomg    schedule 14.04.2020    source sumber
comment
Periksa karakter terakhir dari masukan fgets Anda.   -  person Jongware    schedule 14.04.2020


Jawaban (3)


Anda dapat memeriksa panjang string yang dibaca dan karena fgets juga membaca karakter baris baru, Anda dapat secara eksplisit memeriksa '\n' sebagai karakter masukan terakhir.

char buf[6];
while (fgets(buf, sizeof(buf), stdin)) {
    if (strlen(buf) > 5
        || (strlen(buf) == 5 && buf[strlen(buf) - 1] != '\n')) {
        fprintf(stderr, "line too long\n");
        exit(EXIT_FAILURE);
    }
}    

Buffer harus terdiri dari setidaknya enam karakter: 4 karakter input + 1 karakter baris baru + string yang mengakhiri byte NUL.

person Stephan Schlecht    schedule 14.04.2020
comment
Ini dapat terjadi dengan pengalihan file. - person Nate Eldredge; 14.04.2020

Anda membuat pilihan yang tepat untuk membaca dengan fgets(), satu-satunya aturan praktis yang Anda langgar adalah jangan berhemat pada ukuran buffer. Namun, meskipun Anda melakukannya, Anda dapat menangani semuanya dengan baik dengan fgets().

Saat Anda membaca satu baris dari sebuah file, fgets() (atau POSIX getline()) membaca dan menyertakan '\n' sebagai bagian dari buffer yang diisinya (jika ada ruang). Jika Anda mengharapkan hingga 4 karakter, maka ukuran buffer 5 terlalu pendek untuk menampung semua karakter Anda, karakter nul-terminating, dan '\n'. Keadaan Anda saat mencoba membaca baris 4 karakter ("cats") dengan buffer 5 karakter dengan fgets() akan mengakibatkan buf ditahan:

    +---+---+---+---+---+
    | c | a | t | s | \0|    -->   '\n' remains unread
    +---+---+---+---+---+

Anda juga dapat menanganinya dengan baik (tetapi sebaiknya jangan menghemat ukuran buffer) Untuk menangani masalah ini dengan baik, Anda perlu memeriksa:

  • jika '\n' adalah karakter terakhir dalam buffer, selesaikan pembacaan baris, potong '\n' dengan menimpa karakter nul-terminating;
  • otherwise, read next char;
    • if next char is '\n', then OK, you read all chars and there wasn't room for the '\n' which you just read and checked -- continue reading the next line;
    • lain jika karakter berikutnya adalah EOF, maka Anda membaca semua karakter di baris terakhir dalam file dengan akhir file non-POSIX (tidak ada '\n' setelah baris data terakhir), hentikan loop baca yang Anda temukan EOF;
  • jika tidak, karakter tambahan tetap belum dibaca di baris, baca dan buang karakter hingga '\n' atau EOF berikutnya ditemukan

Dengan menggabungkan logika tersebut, Anda dapat melakukan:

#include <stdio.h>
#include <string.h>

int main (void) {

    char buf[5];

    while (fgets (buf, 5, stdin)) {                 /* read each line */
        if (strchr (buf, '\n'))                     /* if '\n' found - line read */
            buf[strcspn (buf, "\n")] = 0;           /* nul-termiante at '\n' */
        else {  /* otherwise */
            int c = getchar();                      /* read next chars */
            if (c == '\n')                          /* if '\n', OK read next line */
                continue;
            else if (c == EOF)                      /* if EOF, OK, non-POSIX eof */
                break;
            fputs ("error: line too long - discarding remainder.\n", stderr);
            for (; c != '\n' && c != EOF; c = getchar()) {}
        }
    }
}

Periksa semuanya dan beri tahu saya jika Anda memiliki pertanyaan lebih lanjut.

person David C. Rankin    schedule 14.04.2020

Di sini saya membuat fungsi ini untuk membaca file char demi char dan hanya mengembalikan satu baris per panggilan

jadi sekarang Anda dapat membaca file Anda baris demi baris, tipe Line memiliki array karakter value tempat kami menyimpan baris dan int hasNextLine 1 atau 0 (bool) yang memberi tahu Anda apakah file tersebut memiliki baris lain atau tidak, ini berguna saat Anda mengulang file baris demi baris.

#include <stdlib.h>
#include <stdio.h>

typedef struct {
  char *value;
  int hasNextLine;
} Line;

Line * getLine(FILE *file) {
  Line *line = (Line *)malloc(sizeof(Line));
  if(line == NULL) {
    return NULL;
  }
  line->value = NULL;
  line->hasNextLine = 1;
  int n = 0, c;
  while(1) {
    c = getc(file);
    char *tmpStr = (char *)realloc(line->value, n + 2);
    if(tmpStr == NULL) {
      line->hasNextLine = -1;
      return line;
    }
    line->value = tmpStr;
    if(c == EOF) {
      line->hasNextLine = 0;
      line->value[n] = '\0';
      return line;
    }
    if(c == '\n') {
      line->value[n] = '\0';
      return line;
    }
    line->value[n] = c;
    n++;
  }
  return line;
}

Penggunaan:

// example reading one line

int main() {
  FILE *f = fopen("your_file.txt", "r");

  if(f == NULL) {
    printf("File not found!");
    return 1;
  }

  Line *l = getLine(f);

  if(l != NULL) {
    printf("%s\n", l->hasNextLine != -1 ? l->value :
      "Error: while getting the line");
    free(l->value);
    free(l);
  }

  fclose(f);
  return 0;
}
// example reading the whole file

int main() {
  FILE *f = fopen("your_file.txt", "r");

  if(f == NULL) {
    printf("File not found!");
    return 1;
  }

  Line *l;
  int hasNextLine;

  while(1) {
    l = getLine(f);
    if(l != NULL) {
      printf("%s\n", l->hasNextLine != -1 ? l->value :
        "Error: while getting the line");
      free(l->value);
      hasNextLine = l->hasNextLine;
      free(l);
    }
    if(hasNextLine <= 0) {
      break;
    }
  }

  fclose(f);
  return 0;
}

Anda dapat membuat fungsi khusus untuk input pengguna

char * sgetLine(char *msg) {
  printf("%s", msg);
  Line *l = getLine(stdin);
  char *strLine = NULL;
  if(l == NULL) {
    return NULL;
  }else {
    if(l->hasNextLine == -1) {
      free(l->value);
      free(l);
      return NULL;
    }
    strLine = l->value;
    free(l);
    return strLine;
  }
}

jadi sekarang Anda dapat menggunakan satu pemanggilan fungsi untuk mencetak pertanyaan dan mendapatkan jawabannya (char array)

int main() {
  char *l = sgetLine("What is your name? ");
  if(l != NULL) {
    printf("%s\n", l);
  }
  free(l);
  return 0;
}
person SaymoinSam    schedule 14.04.2020