您显示的数据很好且一致 - 6 个数据行和一个记录结束 (EOR) 标记,字段顺序相同。目前尚不清楚是否可以安全地假设所有数据都将受到严格的约束。让我们假设,pro tem,它是。然后您需要读取并累积行到 EOR,然后处理结果数据。
您也没有规定您是否在支持 POSIX getline() 的平台上。我会假设你是因为它让生活更简单。如有必要,您可以使用fgets() 进行操作。
您可以使用以下代码读取 EOR:
static size_t max(size_t x, size_t y) { return (x > y) ? x : y; }
char *get_record(FILE *fp, const char *eor)
{
char *ibuffer = 0;
size_t ibuflen = 0;
char *obuffer = 0;
size_t obuflen = 0;
size_t omaxlen = 0;
ssize_t ilen;
size_t eorlen = strlen(eor);
while ((ilen = getline(&ibuffer, &ibuflen, fp)) != -1)
{
if (obuflen + ilen + 1 >= omaxlen)
{
size_t nbuflen = max(obuflen * 2, obuflen + ilen + 1);
void *nbuffer = realloc(obuffer, nbuflen);
if (nbuffer == 0)
{
free(ibuffer);
free(obuffer);
return 0;
}
obuffer = nbuffer;
omaxlen = nbuflen;
}
memmove(obuffer + obuflen, ibuffer, ilen + 1);
obuflen += ilen;
if (strncmp(ibuffer, eor, eorlen) == 0 && ibuffer[eorlen] == '\n')
break;
}
free(ibuffer);
return obuffer;
}
/* Test harness for get_record() */
int main(void)
{
char *buffer;
while ((buffer = get_record(stdin, "----")) != 0)
{
printf("[[%s]]\n", buffer);
free(buffer);
}
return 0;
}
源文件getrec.c
然后可以扩展以处理来自记录的结构,如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAX_RECORDS = 20 };
enum { MAX_TAG = 20 };
typedef struct
{
char title[80];
char artist[80];
int year;
char genre[80];
char label[80];
double price;
} Record;
static Record record_inventory[MAX_RECORDS];
static size_t n_rec = 0;
static size_t max(size_t x, size_t y) { return (x > y) ? x : y; }
extern char *get_record(FILE *fp, const char *eor);
extern int scan_record(const char *buffer, Record *record);
extern void print_record(size_t i, const Record *record);
char *get_record(FILE *fp, const char *eor)
{
char *ibuffer = 0;
size_t ibuflen = 0;
char *obuffer = 0;
size_t obuflen = 0;
size_t omaxlen = 0;
ssize_t ilen;
size_t eorlen = strlen(eor);
while ((ilen = getline(&ibuffer, &ibuflen, fp)) != -1)
{
if (obuflen + ilen + 1 >= omaxlen)
{
size_t nbuflen = max(obuflen * 2, obuflen + ilen + 1);
void *nbuffer = realloc(obuffer, nbuflen);
if (nbuffer == 0)
{
free(ibuffer);
free(obuffer);
return 0;
}
obuffer = nbuffer;
omaxlen = nbuflen;
}
memmove(obuffer + obuflen, ibuffer, ilen + 1);
obuflen += ilen;
if (strncmp(ibuffer, eor, eorlen) == 0 && ibuffer[eorlen] == '\n')
break;
}
free(ibuffer);
return obuffer;
}
static int scan_tag(const char *tag, const char *data)
{
int pos;
char fmtstr[MAX_TAG];
sprintf(fmtstr, " %%%d[^:]:%%n", MAX_TAG - 1);
char tagstr[MAX_TAG];
if (sscanf(data, fmtstr, tagstr, &pos) != 1)
return 0;
if (strcmp(tagstr, tag) != 0)
return 0;
return pos + 1;
}
static size_t scan_string(const char *tag, const char *data, char *buffer, size_t buflen)
{
int pos1 = scan_tag(tag, data);
if (pos1 == 0)
return 0;
char fmtstr[MAX_TAG];
int pos2;
sprintf(fmtstr, " %%%zu[^\n]%%n", buflen - 1);
if (sscanf(data + pos1, fmtstr, buffer, &pos2) != 1)
return 0;
return (size_t)(pos1 + pos2);
}
static size_t scan_integer(const char *tag, const char *data, int *int_val)
{
int pos1 = scan_tag(tag, data);
if (pos1 == 0)
return 0;
int pos2;
if (sscanf(data + pos1, "%d%n", int_val, &pos2) != 1)
return 0;
return (size_t)(pos1 + pos2);
}
static size_t scan_double(const char *tag, const char *data, double *dbl_val)
{
int pos1 = scan_tag(tag, data);
if (pos1 == 0)
return 0;
int pos2;
if (sscanf(data + pos1, "%lf%n", dbl_val, &pos2) != 1)
return 0;
return (size_t)(pos1 + pos2);
}
int scan_record(const char *buffer, Record *record)
{
size_t offset = 0;
const char *scan_pos = buffer + offset;
if ((offset = scan_string("Title", scan_pos, record->title, sizeof(record->title))) == 0)
return -1;
scan_pos += offset;
if ((offset = scan_string("Artist", scan_pos, record->artist, sizeof(record->artist))) == 0)
return -1;
scan_pos += offset;
if ((offset = scan_integer("Year", scan_pos, &record->year)) == 0)
return -1;
scan_pos += offset;
if ((offset = scan_string("Genre", scan_pos, record->genre, sizeof(record->genre))) == 0)
return -1;
scan_pos += offset;
if ((offset = scan_string("Label", scan_pos, record->label, sizeof(record->label))) == 0)
return -1;
scan_pos += offset;
if ((offset = scan_double("Price", scan_pos, &record->price)) == 0)
return -1;
return 0;
}
void print_record(size_t i, const Record *record)
{
printf("Record: %zu\n", i);
printf("Title: %s\n", record->title);
printf("Artist: %s\n", record->artist);
printf("Year: %4d\n", record->year);
printf("Genre: %s\n", record->genre);
printf("Label: %s\n", record->label);
printf("Price: %.2f\n", record->price);
putchar('\n');
}
int main(void)
{
char *buffer;
int rc = 0;
while ((buffer = get_record(stdin, "----")) != 0 && rc == 0)
{
printf("Input %zu: [[%s]]\n", n_rec, buffer);
rc = scan_record(buffer, &record_inventory[n_rec++]);
free(buffer);
}
for (size_t i = 0; i < n_rec; i++)
print_record(i, &record_inventory[i]);
return 0;
}
示例输入文件data
Title: Void
Artist: RL Grime
Year: 2014
Genre: Bass
Label: We Did It
Price: 14.95
----
Title: Mssingno EP
Artist: Mssingno
Year: 2013
Genre: Grime / Garage
Label: Goon Club Allstars
Price: 10.00
----
样本输出
$ ./getrec < data
Input 0: [[Title: Void
Artist: RL Grime
Year: 2014
Genre: Bass
Label: We Did It
Price: 14.95
----
]]
Input 1: [[Title: Mssingno EP
Artist: Mssingno
Year: 2013
Genre: Grime / Garage
Label: Goon Club Allstars
Price: 10.00
----
]]
Record: 0
Title: Void
Artist: RL Grime
Year: 2014
Genre: Bass
Label: We Did It
Price: 14.95
Record: 1
Title: Mssingno EP
Artist: Mssingno
Year: 2013
Genre: Grime / Garage
Label: Goon Club Allstars
Price: 10.00
$
处理记录元素的可变顺序、缺失元素或额外元素,留作练习。 scan_tag() 函数需要返回它找到的标签,而不是检查它是否被赋予了正确的标签。调用代码必须适应已找到的内容,如果同一标签出现多次则报告错误等。
显示的代码不能很好地报告错误。它发现它们没问题,但它没有报告问题所在。有很多方法可以做到这一点;你必须决定什么是最适合你的。
在提交之前,请确保您了解整个代码中发生的情况;其中一些不是初学者写的那种东西。