我正在学习C并尝试编写程序来保存音乐信息。 我正在阅读包含以下内容的文件:
Hello,Adele,2015
Sorry,Justin Bieber,2015
X Gon Give It To Ya,DMX,2002
尝试进行相当基本的操作 - 查看每行直到EOF,抓取内容,使用','
作为分隔符进行标记,将字符串的每个部分存储到3个变量中,将这3个变量存储到全局使用全局索引的数组,它在main中定义,如
Song SongList[1024];
int globalCounter;
globalCounter = 0;
我写的函数如下所示,其中fp
是成功打开的文件,SongList
是Song
结构的数组,globalCounter
是当前的全局索引数组中的位置。
int getFileData(FILE *fp, Song *SongList, int globalCounter) {
int newCount = globalCounter;
char fileOut[1024];
int lineno = 0;
while (!feof(fp)) {
if (fgets(fileOut, 1024, fp) != NULL) {
newCount++;
char *tokenizer;
char *fileTitle;
char *fileArtist;
char *fileYear;
tokenizer = strtok(fileOut, ",");
int counter = 0;
fileTitle = tokenizer;
counter++;
while (tokenizer != NULL) {
tokenizer = strtok(NULL, ",");
if (counter == 1)
fileArtist = tokenizer;
if (counter == 2)
fileYear = tokenizer;
counter++;
}
SongList[newCount].title = fileTitle;
SongList[newCount].artist = fileArtist;
SongList[newCount].year = fileYear;
// prints the right values
printf("%i\n", newCount);
printf("TITLE: %s\n", SongList[newCount].title);
printf("ARTIST: %s\n", SongList[newCount].artist);
printf("YEAR: %s\n", SongList[newCount].year);
}
}
return newCount;
}
看起来它工作正常,但是,当我尝试在return语句之前进行简单的内容打印时,我得到了垃圾返回数据。
int counter = 0;
while (counter < newCount) {
printf("%s, %s, %s", SongList[newCount].title, SongList[newCount].artist, SongList[newCount].year);
counter++;
}
的产率:
X Gon Give It To Ya, DMX, 2002X Gon Give It To Ya, DMX, 2002X Gon Give It To Ya, DMX, 2002
当我尝试在另一个函数中使用几乎完全相同的while循环时,我得到更多的垃圾数据输出。
(null), (null), (null), , ╨╦a, , DMXm
歌曲结构如下所示
typedef struct Song {
char *title;
char *artist;
char *year;
} Song;
我怀疑这个问题有一个简单的解决方案:与变量的类型定义有关,缺少* /&amp;或者不以&#39; \ 0&#39;结尾。 - 我不确定是什么导致了这一点。
答案 0 :(得分:3)
而不是while (!feof(fp)) { if (fgets(fileOut, 1024, fp) != NULL) {
只使用:
while (fgets(fileOut, 1024, fp) != NULL) {
请注意,使用strtok
进行解析是草率的:艺术家将以空格开头,年份将以空格开头并包含最终换行符。使用指针并手动解析会更精确。
您必须存储字符串的副本:
SongList[newCount].title = strdup(fileTitle);
SongList[newCount].artist = strdup(fileArtist);
SongList[newCount].year = strdup(fileYear);
处理数据时,您可能需要free
这些字符串,除非您退出程序,在这种情况下free
不是必需的,但仍建议帮助跟踪潜在的内存泄漏。
在成功存储条目后,再增加newCount++;
。
根据您的说明,您还应将newCount
存储到全局变量globalCounter
。
您的打印循环应使用counter
而不是newCount
:
int counter = 0;
while (counter < newCount) {
printf("%s, %s, %s\n",
SongList[counter].title,
SongList[counter].artist,
SongList[counter].year);
counter++;
}
以下是更正后的版本:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct Song {
char *title;
char *artist;
char *year;
} Song;
Song SongList[1024];
int globalCounter = 0;
int getFileData(FILE *fp, Song *SongList, int *globalCounter) {
int newCount = *globalCounter;
char fileOut[1024];
while (newCount < 1024 && fgets(fileOut, 1024, fp) != NULL) {
char *p = fileOut;
char *fileTitle;
char *fileArtist;
char *fileYear;
fileTitle = p;
if ((p = strchr(p, ',')) == NULL)
continue;
*p++ = '\0';
p += strspn(p, " \t"); /* skip blank characters */
fileArtist = p;
if ((p = strchr(p, ',')) == NULL)
continue;
*p++ = '\0';
p += strspn(p, " \t"); /* skip blank characters */
fileYear = p;
p += strcspn(p, ",\n"); /* skip to ',' or '\n' or end of string */
*p = '\0';
SongList[newCount].title = strdup(fileTitle);
SongList[newCount].artist = strdup(fileArtist);
SongList[newCount].year = strdup(fileYear);
// prints the right values
printf("%i\n", newCount);
printf("TITLE: %s\n", SongList[newCount].title);
printf("ARTIST: %s\n", SongList[newCount].artist);
printf("YEAR: %s\n\n", SongList[newCount].year);
newCount++;
}
return *globalCounter = newCount;
}
int main() {
FILE *fin;
if ((fin = fopen("test.txt", "r")) != NULL) {
getFileData(fin, SongList, &globalCounter);
fclose(fin);
}
for (int counter = 0; counter < globalCounter; counter++) {
printf("%s, %s, %s\n",
SongList[counter].title,
SongList[counter].artist,
SongList[counter].year);
counter++;
}
return 0;
}
答案 1 :(得分:1)
您需要复制字符串。你不只是保持指向strtok返回的指针。 (并且您应该检查strtok和strdup的返回值是否有错误,并在完成Song记录时释放内存)。 您过早地迭代索引(跳过索引0) 您对返回值的测试是在for循环中索引相同的最后位置。
#include <stdio.h>
#include <string.h>
typedef struct Song
{
char* title;
char* artist;
char* year;
} Song;
Song SongList[1024];
int globalCounter = 0;
int getFileData(FILE* fp, Song* SongList, int globalCounter)
{
int newCount = globalCounter;
char fileOut[1024];
int lineno = 0;
while (!feof(fp))
{
if (fgets(fileOut,1024,fp) != NULL)
{
char *tokenizer;
//char* fileTitle;
//char* fileArtist;
//char* fileYear;
tokenizer = strtok(fileOut, ",");
int counter = 0;
SongList[newCount].title = strdup( tokenizer );
//fileTitle = tokenizer;
counter++;
while(tokenizer != NULL)
{
tokenizer = strtok(NULL, ",");
if(counter == 1)
SongList[newCount].artist = strdup( tokenizer );
// fileArtist = tokenizer;
if(counter == 2)
SongList[newCount].year = strdup( tokenizer );
// fileYear = tokenizer;
counter++;
}
//SongList[newCount].title = fileTitle;
//SongList[newCount].artist = fileArtist;
//SongList[newCount].year = fileYear;
// prints the right values
printf("%i\n",newCount);
printf("TITLE: %s\n", SongList[newCount].title);
printf("ARTIST: %s\n", SongList[newCount].artist);
printf("YEAR: %s\n", SongList[newCount].year);
newCount++;
}
}
return newCount;
}
int main()
{
FILE * fin = fopen( "test.txt", "rt" );
int newCount = getFileData( fin, SongList, globalCounter );
int counter = 0;
while(counter < newCount){
printf("%s, %s, %s",SongList[counter].title,SongList[counter].artist,SongList[counter].year);
counter++;
}
}
测试:
1212:/tmp$ g++ test.cpp && ./a.out
0
TITLE: Hello
ARTIST: Adele
YEAR: 2015
1
TITLE: Sorry
ARTIST: Justin Bieber
YEAR: 2015
2
TITLE: X Gon Give It To Ya
ARTIST: DMX
YEAR: 2002
Hello, Adele, 2015
Sorry, Justin Bieber, 2015
X Gon Give It To Ya, DMX, 2002