对于给定的表示,
typedef struct {
int age;
char *firstName;
char *lastName;
}Record;
并给予file.txt
,
Age,LastName,FirstName
50,B,A
30,A,B
20,X,D
10,F,A
90,V,E
60,N,M
以下是main()
,
pFile=fopen("file.txt", "r");
...
//Complete file is copied to 'readBuffer', approach inspired from
// http://stackoverflow.com/a/11487960/3317808
....
char *record = strtok(readBuffer,"\n"); //Ignore header record
record = strtok(NULL, "\n");// Read first data record(50,'B','A')
for(;record != NULL; record = strtok(NULL,"\n")){
printf("###Print complete record\n");
puts(record);
Record *r = malloc(sizeof(Record)*1);
r->age = atoi(strtok(record,","));
char *firstName = strtok(NULL,",");
char *lastName = strtok(NULL, ",");
r->firstName = strdup(firstName);
r->lastName = strdup(lastName);
printf("Age: %d\n", r->age);
printf("First name: %s\n", r->firstName);
printf("Last name: %s\n", r->lastName);
}
strtok(readBuffer,",")
将编译器与for循环
strtok(record,",")
混淆
实际输出显示只有一条记录发生了标记化。
$ ./program.exe
Print complete file
Age,LastName,FirstName
50,B,A
30,A,B
20,X,D
10,F,A
90,V,E
60,N,M
###Print complete record
50,B,A
Age: 50
First name: B
Last name: A
如何解决?
答案 0 :(得分:4)
如果在我们的情况下可行,使用strtok_r()
似乎是最简单的方法。只是告知,这不是标准 C,它在POSIX中。
来自man page,
strtok_r()
函数是可重入版本strtok()
。saveptr
参数是指向char *
内部strtok_r()
内部使用的strtok_r()
变量的指针,以便在解析相同字符串的连续调用之间维护上下文。
和
可以使用指定
saveptr
个不同{{1}}参数的{{1}}调用序列同时解析不同的字符串。
该手册页还有一个您正在寻找的场景的示例。
答案 1 :(得分:2)
正如@David C. Rankin建议的那样,使用fgets
和strtok
来读取每一行是解决此问题的好方法。
如果您希望将来使用mergesort
,那么使用此排序算法最容易将数据存储在结构数组中。此外,如果您不知道文件中将包含多少行,则可能需要在运行时动态分配该行。
您可以在文件中存储较低级别struct
:
typedef struct {
int age;
char *firstname;
char *lastname;
} record_t;
存储文件所有内容的更高级别struct
:
typedef struct {
record_t *records; /* pointer to record_t */
char *headers; /* pointer holding header */
size_t currsize; /* current status of information being added */
size_t lastidx;
} allrecords_t;
\n
之前的缓冲区末尾添加\0
个字符。可以轻松删除附加的\n
。NULL
。如果达到EOF
并且没有读取任何字符,那么这也会返回NULL
。stdin
或FILE *
的指定流中读取。 使用fgets()
时,您可以调用一次以使用标头信息:
fgets(buffer, 256, pfile); /* error checking needed */
然后,您可以在while()
循环中再次调用它,以使用文件中的其余数据:
while (fgets(buffer, 256, pfile) != NULL) {
....
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Constants used */
#define INITSIZE 20
#define BUFFSIZE 256
#define MALLOC_MSG "Allocation"
#define REALLOC_MSG "Reallocation"
/* array of structs setup */
typedef struct {
int age;
char *firstname;
char *lastname;
} record_t;
typedef struct {
record_t *records;
char *headers;
size_t currsize;
size_t lastidx;
} allrecords_t;
/* function prototypes */
allrecords_t *initialize_records(void);
void read_header(FILE *filestream, allrecords_t *Record, char buffer[]);
void read_data(FILE *filestream, allrecords_t *Record, char buffer[]);
void print_records(allrecords_t *Record);
void check_ptr(void *ptr, const char *msg);
void remove_newline(char buffer[]);
int main(void) {
FILE *fp;
allrecords_t *Record;
/* static buffer for fgets() */
char buffer[BUFFSIZE];
fp = fopen("fileex.txt", "r");
if (!fp) {
fprintf(stderr, "Cannot read file.\n");
exit(EXIT_FAILURE);
}
Record = initialize_records();
/* Reads the first line */
read_header(fp, Record, buffer);
/* Reads next lines */
read_data(fp, Record, buffer);
/* prints and frees structure elements*/
print_records(Record);
return 0;
}
/* function which reads the age/firstname/lastname data */
void read_data(FILE *filestream, allrecords_t *Record, char buffer[]) {
char *data; /* only need one char *pointer for strtok() */
const char *delim = ",";
while (fgets(buffer, BUFFSIZE, filestream) != NULL) {
remove_newline(buffer); /* optional to remove '\n' */
/* resize array when necessary */
if (Record->currsize == Record->lastidx) {
Record->currsize *= 2;
Record->records = realloc(Record->records, Record->currsize * sizeof(record_t));
check_ptr(Record->records, REALLOC_MSG);
}
/* adding info to array */
/* using strdup() will lead to less code here */
data = strtok(buffer, delim);
Record->records[Record->lastidx].age = atoi(data);
data = strtok(NULL, delim);
Record->records[Record->lastidx].firstname = malloc(strlen(data)+1);
check_ptr(Record->records[Record->lastidx].firstname, MALLOC_MSG);
strcpy(Record->records[Record->lastidx].firstname, data);
data = strtok(NULL, delim);
Record->records[Record->lastidx].lastname = malloc(strlen(data)+1);
check_ptr(Record->records[Record->lastidx].lastname, MALLOC_MSG);
strcpy(Record->records[Record->lastidx].lastname, data);
Record->lastidx++;
}
}
/* prints and frees all members safely, without UB */
void print_records(allrecords_t *Record) {
size_t i;
printf("\nComplete Record:\n");
printf("%s\n", Record->headers);
free(Record->headers);
Record->headers = NULL;
for (i = 0; i < Record->lastidx; i++) {
printf("%d,%s,%s\n", Record->records[i].age,
Record->records[i].firstname,
Record->records[i].lastname);
free(Record->records[i].firstname);
Record->records[i].firstname = NULL;
free(Record->records[i].lastname);
Record->records[i].lastname = NULL;
}
free(Record->records);
Record->records = NULL;
free(Record);
Record = NULL;
}
/* function which only reads header */
void read_header(FILE *filestream, allrecords_t *Record, char buffer[]) {
if (fgets(buffer, BUFFSIZE, filestream) == NULL) {
fprintf(stderr, "Error reading header.\n");
exit(EXIT_FAILURE);
}
remove_newline(buffer);
Record->headers = malloc(strlen(buffer)+1);
check_ptr(Record->headers, MALLOC_MSG);
strcpy(Record->headers, buffer);
}
/* function which removes '\n', lots of methods to do this */
void remove_newline(char buffer[]) {
size_t slen;
slen = strlen(buffer);
/* safe way to remove '\n' and check for bufferoverflow */
if (slen > 0) {
if (buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
printf("Buffer overflow detected.\n");
exit(EXIT_FAILURE);
}
}
}
/* initializes higher level struct */
allrecords_t *initialize_records(void) {
allrecords_t *Record = malloc(sizeof(*Record));
check_ptr(Record, MALLOC_MSG);
Record->currsize = INITSIZE;
Record->headers = NULL;
Record->records = malloc(Record->currsize * sizeof(record_t));
check_ptr(Record->records, MALLOC_MSG);
Record->lastidx = 0;
return Record;
}
/* instead of checking for 'ptr == NULL' everywhere, just call this function */
void check_ptr(void *ptr, const char *msg) {
if (!ptr) {
printf("Null pointer returned: %s\n", msg);
exit(EXIT_FAILURE);
}
}
注意:我使用malloc()
+ strcpy()
代替strdup()
,因为它们来自标准C库,如<string.h>
和{{1而不是POSIX C.
节目输出:
<stdlib.h>
答案 2 :(得分:0)
问题更多的是与逻辑有关,而不是使用strtok。
另外值得注意的是您正在阅读的记录的格式,您在姓氏字段后面没有逗号的问题。
以下代码将实现您的问题
{
char str[80] = "Age,LastName,FirstName\n50,B,A\n30,A,B\n20,X,D\n";
const char newline[2] = "\n";
const char comma[2] = ",";
/* get over the header fields */
strtok(str, newline);
/* walk through other tokens */
for(;;)
{
Record *r = (Record*)malloc(sizeof(Record)*1);
char *age = strtok(NULL, comma);
if(age != NULL)
{
r->age = atoi(age);
char *firstName = strtok(NULL, comma);
char *lastName = strtok(NULL, newline);
r->firstName = (char *)strdup(firstName);
r->lastName = (char *)strdup(lastName);
printf("Age: %d\n", r->age);
printf("First name: %s\n", r->firstName);
printf("Last name: %s\n", r->lastName);
}
else
break;
}
return(0);
}