อ่านบรรทัดจากไฟล์ใน C และแยกจำนวนอินพุต
ฉันมีไฟล์input.dat ในไฟล์นี้มี 3 บรรทัด:
1 2 3
5 7 10 12
8 9 14 13 15 17
ฉันจะอ่านหนึ่งในสามบรรทัดโดยใช้ C และส่งคืนจำนวนองค์ประกอบ ตัวอย่างเช่นผมต้องการที่จะอ่านบรรทัดที่ 2 5 7 10 12
ในหน่วยความจำและยังกลับจำนวนของค่าในบรรทัดที่ 2 4
ซึ่งเป็น รหัสของฉันอยู่ด้านล่าง ...
#include <stdio.h>
#include <stdlib.h>
#define STRING_SIZE 2000
int main() {
FILE *fp = fopen("in.dat", "r");
char line[STRING_SIZE];
int lcount = 0, nline = 1, sum = 0, number;
if (fp != NULL) {
while (fgets(line, STRING_SIZE, fp) != NULL) {
if (lcount == nline) {
while (sscanf(line, "%d ", &number)) {
sum++;
}
break;
} else {
lcount++;
}
}
fclose(fp);
}
exit(0);
}
เมื่อฉันเรียกใช้รหัสนี้มันไม่เคยหยุดนิ่งเหมือนลูปตาย ปัญหาที่นี่คืออะไร?
คำตอบ
ลูปwhile (sscanf(line, "%d ", &number))
จะแยกวิเคราะห์ตัวเลขแรกในบรรทัด
คุณควรใช้strtol
แทน:
#include <stdio.h>
#include <stdlib.h>
#define STRING_SIZE 2000
int main() {
FILE *fp = fopen("in.dat", "r");
char line[STRING_SIZE];
int lcount = 0, nline = 1;
if (fp != NULL) {
while (fgets(line, STRING_SIZE, fp) != NULL) {
if (lcount == nline) {
char *p = line, *q;
int count = 0;
for (;;) {
long val = strtol(p, &q, 0); // parse an integer
if (q == p) {
// end of string or not a number
break;
}
// value was read into val. You can use it for whatever purpose
count++;
p = q;
}
printf("%d\n", count);
break;
} else {
lcount++;
}
}
fclose(fp);
}
return 0;
}
คุณมีความคิดไปตามเส้นทางที่เหมาะสมกับการใช้งานของsscanf()
ชิ้นเดียวของปริศนาที่คุณกำลังขาดหายไปคือวิธีการที่จะใช้การชดเชยเพื่อให้คุณอ่านค่าในบรรทัดถัดไปที่มีการเรียกร้องต่อไปยังline
sscanf()
คุณทำได้โดยการติดตามจำนวนอักขระที่ใช้ในการโทรแต่ละครั้งเพื่อsscanf()
ใช้การ"%n"
แปลง (ไม่ได้เพิ่มจำนวน Conversion ของคุณที่ส่งคืนโดยsscanf()
) ตัวอย่างเช่นการอ่านบรรทัดจากสตรีมไฟล์ที่เปิดอยู่fp
คุณสามารถทำได้:
#define MAXC 1024 /* if you need a constant, #define one (or more) */
...
char line[MAXC] = ""; /* buffer to hold each line */
...
while (fgets (line, MAXC, fp)) { /* reach each line in file */
int offset = 0, /* offset in line for next sscanf() read */
nchr = 0, /* number of char consumed by last read */
val, /* integer value read with sscanf() */
nval = 0; /* number of values read in line */
/* conververt each integer at line + offset, saving no. of chars consumed */
while (sscanf (line + offset, "%d%n", &val, &nchr) == 1) {
printf (" %d", val); /* output value read */
offset += nchr; /* update offset with no. chars consumend */
nval++; /* increment value count */
}
printf (" - %d values\n", nval); /* output no. values in line */
}
( หมายเหตุ: strtol()
ให้การรายงานข้อผิดพลาดที่ดีกว่าsscanf()
การแปลงที่ล้มเหลว)
หากคุณรวมเข้ากับตัวอย่างที่อ่านจากชื่อไฟล์ที่ให้ไว้เป็นอาร์กิวเมนต์แรกของโปรแกรม (หรืออ่านจากstdin
ค่าเริ่มต้นหากไม่มีการระบุอาร์กิวเมนต์) คุณสามารถทำได้:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char line[MAXC] = ""; /* buffer to hold each line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (line, MAXC, fp)) { /* reach each line in file */
int offset = 0, /* offset in line for next sscanf() read */
nchr = 0, /* number of char consumed by last read */
val, /* integer value read with sscanf() */
nval = 0; /* number of values read in line */
/* conververt each integer at line + offset, saving no. of chars consumed */
while (sscanf (line + offset, "%d%n", &val, &nchr) == 1) {
printf (" %d", val); /* output value read */
offset += nchr; /* update offset with no. chars consumend */
nval++; /* increment value count */
}
printf (" - %d values\n", nval); /* output no. values in line */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
ตัวอย่างการใช้ / ผลลัพธ์
ด้วยข้อมูลที่คุณแสดงในชื่อไฟล์dat/nvals.txt
คุณจะได้รับ:
$ ./bin/fgetsnvals dat/nvals.txt
1 2 3 - 3 values
5 7 10 12 - 4 values
8 9 14 13 15 17 - 6 values
ตรวจสอบสิ่งต่างๆและแจ้งให้เราทราบหากคุณมีคำถามเพิ่มเติม
คำตอบของchqrlieเวอร์ชันที่สะอาดกว่าเล็กน้อย เริ่มต้นด้วยสตริงนั่นคือสิ่งที่คำถามเกี่ยวกับหลังจากfgets()
นั้น
sscanf()
จะไม่ก้าวผ่านสตริงมันจะอ่านตั้งแต่ต้นเสมอ
strtol()
มองหาlong int
จุดเริ่มต้นของสตริงโดยไม่สนใจช่องว่างเริ่มต้น ให้กลับที่อยู่ของตำแหน่งที่หยุดการสแกน
คู่มือของstrtol()
ระบุว่าควรตรวจสอบข้อผิดพลาดในการแปลงข้อผิดพลาด
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define STRING_SIZE 2000
int main(void)
{
char line[STRING_SIZE] = "5 7 10 12";
char* start = line;
char* end;
int count = 0;
while(1)
{
/**
* strtol() look for long int in beginning of the string
* Ignores beginning whitespace
*
* start: where to strtol() start looking for long int
* end: where strtol() stops scanning for long int
*/
errno = 0; // As strol() manual says
strtol(start, &end, 0);
if (errno != 0)
{
printf("Error in strtol() conversion.\n");
exit(0);
}
if (start == end) break; // Quit loop
start = end;
count++;
}
printf("count: %d\n", count);
return 0;
}