ใน C ฉันจะสร้างข้อผิดพลาดได้อย่างไรหากสตริงอินพุตใหญ่เกินไป

ฉันต้องการอ่านรายการคำจากไฟล์ซึ่งมีหนึ่งคำต่อบรรทัด คำควรมีอักขระไม่เกิน 4 ตัวต่อคำ ฉันจะสร้างข้อผิดพลาดได้อย่างไรหากบรรทัดใดบรรทัดหนึ่งยาวกว่านั้น

ฉันลองอ่านคำศัพท์โดยใช้ fgets

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

และด้วย scanf

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

แต่ในทั้งสองกรณีจะแยกเส้นยาวออกเป็นเส้นเล็ก เช่น qwerasdf อ่านเป็นสองคำ qwer และ asdf มีวิธีตรวจสอบหรือไม่ว่าพยายามอ่านบรรทัดยาวที่มีอักขระมากกว่า 4 ตัวแล้วเกิดข้อผิดพลาดแทน

ทางเลือกเดียวที่ฉันคิดได้คือการอ่านอักขระที่ป้อนทีละอักขระและดูแลทุกอย่างด้วยตัวเอง แต่มีวิธีแก้ไขปัญหาที่ง่ายกว่านี้หรือไม่โดยใช้ฟังก์ชันจากไลบรารีมาตรฐาน


person hugomg    schedule 14.04.2020    source แหล่งที่มา
comment
ตรวจสอบอักขระตัวสุดท้ายของอินพุต fgets ของคุณ   -  person Jongware    schedule 14.04.2020


คำตอบ (3)


คุณสามารถตรวจสอบความยาวของสตริงการอ่านได้ และเนื่องจาก fgets อ่านอักขระขึ้นบรรทัดใหม่ด้วย คุณจึงตรวจสอบ '\n' เป็นอักขระอินพุตตัวสุดท้ายได้อย่างชัดเจน

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);
    }
}    

บัฟเฟอร์ต้องประกอบด้วยอักขระอย่างน้อยหกตัว: อักขระอินพุต 4 ตัว + อักขระขึ้นบรรทัดใหม่ 1 ตัว + สตริงที่สิ้นสุดไบต์ NUL

person Stephan Schlecht    schedule 14.04.2020
comment
สามารถเกิดขึ้นได้กับการเปลี่ยนเส้นทางไฟล์ - person Nate Eldredge; 14.04.2020

คุณกำลังตัดสินใจเลือกการอ่านด้วย fgets() หลักการสำคัญเพียงอย่างเดียวที่คุณกำลังฝ่าฝืนคือ อย่าข้ามขนาดบัฟเฟอร์ แต่แม้ว่าคุณจะทำเช่นนั้น คุณก็สามารถจัดการสิ่งต่างๆ ได้อย่างถูกต้องด้วย fgets()

เมื่อคุณอ่านบรรทัดจากไฟล์ fgets() (หรือ POSIX getline()) จะอ่านและรวม '\n' เป็นส่วนหนึ่งของบัฟเฟอร์ที่เติมไว้ (หากมีที่ว่าง) หากคุณคาดหวังให้มีอักขระสูงสุด 4 ตัว แสดงว่าขนาดบัฟเฟอร์ 5 นั้นสั้นเกินไปสำหรับอักขระทั้งหมดของคุณ อักขระ สิ้นสุดด้วย nul และ '\n' กรณีของคุณที่พยายามอ่านบรรทัด 4 อักขระ ("cats") โดยมีบัฟเฟอร์ 5 อักขระที่มี fgets() จะส่งผลให้มีการถือครอง buf:

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

คุณสามารถจัดการสิ่งนั้นได้อย่างสวยงามเช่นกัน (แต่อย่า ปล่อยเกินขนาดบัฟเฟอร์) หากต้องการจัดการปัญหาอย่างงดงาม คุณต้องตรวจสอบ:

  • ถ้า '\n' เป็นอักขระตัวสุดท้ายในบัฟเฟอร์ ให้อ่านบรรทัดให้สมบูรณ์ ตัด '\n' ด้วยการเขียนทับด้วยอักขระ สิ้นสุด nul
  • 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;
    • มิฉะนั้น หากอักขระถัดไปคือ EOF แสดงว่าคุณอ่านอักขระทั้งหมดในบรรทัดสุดท้ายในไฟล์ที่มีจุดสิ้นสุดไฟล์ที่ไม่ใช่ POSIX (ไม่ใช่ '\n' หลังบรรทัดสุดท้ายของข้อมูล) ให้แยกลูปการอ่านที่คุณพบ EOF;
  • มิฉะนั้นอักขระเพิ่มเติมจะยังไม่ได้อ่านในบรรทัด อ่านและละทิ้งอักขระจนกว่าจะพบ '\n' หรือ EOF ถัดไป

เมื่อนำตรรกะนั้นมารวมกัน คุณก็สามารถทำได้:

#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()) {}
        }
    }
}

ตรวจสอบสิ่งต่างๆ และแจ้งให้เราทราบหากคุณมีคำถามเพิ่มเติม

person David C. Rankin    schedule 14.04.2020

ที่นี่ฉันสร้างฟังก์ชันนี้เพื่ออ่านไฟล์ char โดย char และส่งคืนเพียงบรรทัดเดียวต่อการโทร

ตอนนี้คุณสามารถอ่านไฟล์ของคุณทีละบรรทัดได้ ประเภท Line มีอาร์เรย์ของตัวอักษร value ที่เราจัดเก็บบรรทัดและ int hasNextLine 1 หรือ 0 (บูล) ที่บอกคุณว่าไฟล์มีบรรทัดอื่นหรือไม่มี ซึ่งสะดวกมาก เมื่อคุณวนซ้ำไฟล์ทีละบรรทัด

#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;
}

การใช้งาน:

// 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;
}

คุณสามารถสร้างฟังก์ชันแบบกำหนดเองสำหรับการป้อนข้อมูลของผู้ใช้ได้

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;
  }
}

ตอนนี้คุณสามารถใช้การเรียกใช้ฟังก์ชันเพียงครั้งเดียวเพื่อพิมพ์คำถามและรับคำตอบ (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