C语言中的输出问题

3
我有一个关于make[]数组输出的问题。所以,我首先对名为Carss.txt的文件进行标记化处理,仅获取第三列和第五列,并将结果分别存储在make[]数组和year[]数组中。
我测试了make[]数组,看看它是否会输出我想要的结果。它在while(fgets)循环内部确实做到了,但是当我在while(fgets)之外进行测试时,它会重复输出。
我不明白问题出在哪里。我的程序和文本文件如下。 while(fgets)循环内make数组的输出:(我想要的)
FORD
FORD
JAYCO

while(fgets)循环之外创建数组的输出结果:(不是我想要的)

JAYCO
JAYCO
JAYCO

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
    FILE * fp;
    char * filename = "Carss.txt";
    char lines[100000], blank[100000];
    char * token, * del = " ";
    int i = 0, c, d = 0, j;
    char * make[10000];
    char * year[10000];
    if ((fp = fopen(filename, "r")) == NULL) /* Opens the file */ {
        printf("can't open %s\n", filename);
        exit(1);
    }
    c = 0;
    while (fgets(lines, sizeof(lines), fp) != NULL) {
        token = strtok(lines, "\t");
        i = 1;
        while (i < 5) {
            token = strtok(NULL, "\t");
            if (i == 2) {
                make[c] = token;
            }
            if (i == 4) {
                year[c] = token;
            }
            i++;
        }
        printf("%s\n", make[c]); /* OUTPUT OF THE ARRAY HERE IS WHAT I WANTED */
        c++;
    }
    c = 0;
    while (c < 3) {
        printf("%d\n", c);
        printf("%s\n", make[c]);
        c++;
    }
}

The Text File I tokenize: Carss.txt

1   02V288000   FORD    FOCUS   2001    02S41   ELECTRICAL SYSTEM:BATTERY:CABLES    FORD MOTOR COMPANY  19990719    20010531    V   291854  20030210    ODI Ford Motor Company  20021106    20021106            CERTAIN PASSENGER VEHICLES EQUIPPED WITH ZETEC ENGINES, LOOSE OR BROKEN ATTACHMENTS AND MISROUTED BATTERY CABLES COULD LEAD TO CABLE INSULATION DAMAGE. THIS, IN TURN, COULD CAUSE THE BATTERY CABLES TO SHORT RESULTING IN HEAT DAMAGE TO THE CABLES.  BESIDES HEAT DAMAGE, THE "CHECK ENGINE" LIGHT MAY ILLUMINATE, THE VEHICLE MAY FAIL TO START, OR SMOKE, MELTING, OR FIRE COULD ALSO OCCUR.   DEALERS WILL INSPECT THE BATTERY CABLES FOR THE CONDITION OF THE CABLE INSULATION AND PROPER TIGHTENING OF THE TERMINAL ENDS.  AS NECESSARY, CABLES WILL BE REROUTED, RETAINING CLIPS INSTALLED, AND DAMAGED BATTERY CABLES REPLACED.   OWNER NOTIFICATION BEGAN FEBRUARY 10, 2003.   OWNERS WHO DO NOT RECEIVE THE FREE REMEDY  WITHIN A REASONABLE TIME SHOULD CONTACT FORD AT 1-866-436-7332.    ALSO CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236). 000015339000215022000000202
2   02V288000   FORD    FOCUS   2000    02S41   ELECTRICAL SYSTEM:BATTERY:CABLES    FORD MOTOR COMPANY  19990719    20010531    V   291854  20030210    ODI Ford Motor Company  20021106    20021106            CERTAIN PASSENGER VEHICLES EQUIPPED WITH ZETEC ENGINES, LOOSE OR BROKEN ATTACHMENTS AND MISROUTED BATTERY CABLES COULD LEAD TO CABLE INSULATION DAMAGE. THIS, IN TURN, COULD CAUSE THE BATTERY CABLES TO SHORT RESULTING IN HEAT DAMAGE TO THE CABLES.  BESIDES HEAT DAMAGE, THE "CHECK ENGINE" LIGHT MAY ILLUMINATE, THE VEHICLE MAY FAIL TO START, OR SMOKE, MELTING, OR FIRE COULD ALSO OCCUR.   DEALERS WILL INSPECT THE BATTERY CABLES FOR THE CONDITION OF THE CABLE INSULATION AND PROPER TIGHTENING OF THE TERMINAL ENDS.  AS NECESSARY, CABLES WILL BE REROUTED, RETAINING CLIPS INSTALLED, AND DAMAGED BATTERY CABLES REPLACED.   OWNER NOTIFICATION BEGAN FEBRUARY 10, 2003.   OWNERS WHO DO NOT RECEIVE THE FREE REMEDY  WITHIN A REASONABLE TIME SHOULD CONTACT FORD AT 1-866-436-7332.    ALSO CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236). 000015339000215021000000202
3   02V236000   JAYCO   FT EAGLE 10 SG  2003        EQUIPMENT:OTHER:LABELS  JAYCO, INC. 20020730    20020813    V   86  20020923    MFR Jayco, Inc. 20020904    20020912            ON CERTAIN FOLDING TENT CAMPERS, THE FEDERAL CERTIFICATION (AND RVIA) LABELS HAVE THE INCORRECT GROSS VEHICLE WEIGHT RATING, TIRE SIZE, AND INFLATION PRESSURE LISTED.  IF THE TIRES WERE INFLATED TO 80 PSI, THEY COULD BLOW RESULTING IN A POSSIBLE CRASH.    OWNERS WILL BE MAILED CORRECT LABELS FOR INSTALLATION ON THEIR VEHICLES.   OWNER NOTIFICATION BEGAN SEPTEMBER 23, 2002.    OWNERS SHOULD CONTACT JAYCO AT 1-877-825-4782.   ALSO, CUSTOMERS CAN CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236).  000015210000106403000000349
4个回答

4

当你执行以下操作时:

token = strtok(NULL, "\t");
if(i==2)
{
    make[c] = token;
}

你并不是复制字符串,而只是指向它的指针引用。下一次调用fgets时,该字符串将被覆盖,并且所有引用都指向新字符串。因此,当程序结束时,你的所有指针都指向内存中最后写入的那个东西。
尝试这样做:
make[c] = strdup(token);

这将分配新的内存、复制字符串并返回新的指针。拷贝不会被覆盖。strdup 内部调用 malloc,因此在完成操作后应该使用 free 释放内存。


1
在循环内部,make[c] = token 记录了 char lines[] 中的一个地址。由于该缓冲区在下一次迭代时被覆盖,因此您需要将令牌复制到其他地方以保持安全。
只需使用 make[c] = strdup(token)(年份也是如此)即可完成此操作 - 但请记住,在任何真实的程序中,您应该在使用完所有 make 和 year 字符串后现在释放它们。

0

strtok操作静态缓冲区

当你编写代码时

strtok(lines, "\t");

lines缓冲区被复制到一个内部静态缓冲区 - strtok只有一个静态缓冲区 - 所以每次while循环迭代时,新内容都会被放入静态缓冲区,使任何先前的指针(即make[]和year[])无效。

为了保留标记,您需要为它们分配单独的内存,该内存在循环结束时不会被销毁。这可以通过多种方式完成,例如使用其他帖子中提到的每个字符串上的strdup,尽管那样会有很多簿记,然后释放内存。您还可以使用另一种数据结构,例如链表来存储值,而不是拥有大量指针:

typedef struct record
{
  char make[32]; // lets say the max length of make is 32
  char year[10];  
  struct record* next;
} record;

每当从文件中读取新的一行时,创建一个新记录:
record* first = NULL;
record* last = NULL; 

while (fgets(lines, sizeof(lines), fp) != NULL) 
{
  // first create the record
  record* newRecord = malloc(sizeof(record));
  newRecord->next = NULL;

  // parse the line
  int column = 0;
  for (char* token = strtok(lines,"\t"); token != NULL; token = strtok(NULL, "\t"), ++column)
  {
    switch(column)
    {
    case 2: 
      strncpy(newRecord->make, token, sizeof(newRecord.make));
      break;
    case 4:
      strncpy(newRecord->year, token, sizeof(newRecord.year));
      break;
    default:  // ignore
      break;
    }
  }
  // put in the list
  if (first == NULL)
  {
    first = last = newRecord;
  }
  else
  {
    last->next = newRecord;
    last = newRecord;
  }
}

现在你已经将感兴趣的部分列成了列表。你可以使用以下代码打印出该列表:

for (record* rec = first; rec != NULL; rec = rec->next)
{
  printf( "%s, %s\n", rec->make, rec->year );
}

// 免责声明,我还没有编译过这个。


0
你正在使用错误的数据结构来存储你的结果。
`make` 数组是一个指针数组。这意味着你在那里存储的只是一个内存地址,在你的情况下,是 `strtok` 操作期间使用的指针的内存地址。这不是你想要的,因为你不知道之后会发生什么;在这种情况下,似乎在每次调用函数时它都被覆盖了。
你需要使用多维数组或使用 `malloc()` 或 `strdup()` 来创建一个 `char *` 值的副本,就像 `strtok()` 返回的一样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接