【问题标题】:Inputting data from a file into a data structure将文件中的数据输入数据结构
【发布时间】:2014-12-09 21:45:18
【问题描述】:

我对 C 编程相当陌生,我想做的是将文件中的数据输入到数据结构中。我意识到我有一些错误,我无法弄清楚。任何帮助将不胜感激。

我正在读取的文本文件包含以下内容:

0001:0002:0003:0021:CLS  
0001:0010:0003:0021:CLS  
0001:0002:0002:0080:<HTML>  
0005:0002:0002:8080:<BR>  
0005:0012:0002:8080:<BR>  

我的代码是:

int main() {  
banner(); //call on banner  
int exists(const char *filename);  
const char s[2] = ":";  
char filename[500];  
FILE * inputFile;  
FILE * outputFile;  
char *token;  
int n;  
struct Packet;  
Packet *P;  


printf("Input the file name: ");  
    scanf("%s",filename);  
    printf("\n");



    inputFile = fopen(filename, "r");    
    if (inputFile != NULL)    
    {  
        printf("This file exists");      
    }  
    else   
    {  
        printf("This file doesn't exist\n");    


while(P >=sizeof(Packet)){    
           P=(struct Packet*)calloc( 5, sizeof(Packet *) );  
            fscanf(inputFile,"%s %d %d %d", &P->S, &P->D, &P->T, &P->P, &P->D );  
            fprintf(inputFile,"%s %d %d %d %d\n", P->S, P->D, P->T, P->P, P->D);  
        }  


  while(!feof(inputFile)){
        {
            P=(Packet*)calloc( 5, sizeof(struct Packet *) );
            fscanf(inputFile,"%s %d %d %d", P->S, P->D, P->T, P->P, P->D );
            fprintf(inputFile,"%d %d %d %d %s\n", P->S, P->D, P->T, P->P, P->D);
        }
    }

【问题讨论】:

  • 错误是......?我们可以在头脑中执行一些代码,但我们不会为您运行您的程序……
  • 您好,抱歉,这些错误是警告,它们是否相关,或者您要我发布这些错误吗?
  • 当然它们是相关的。
  • 指针和整数之间的比较,来自不兼容的指针类型的赋值,格式 %s 需要 char 类型的参数,但参数 3 的类型为 int,格式参数太多这些是用逗号分隔的主要警告跨度>
  • P 未初始化,您在 while 循环中错误地使用了它。您正在将指针指向的地址与 Packet 结构的大小(如果打包为 8060 字节)进行比较。接下来,您将分配五个内存指针大小(如果是 64 位指针,则为 40 个字节),这与分配 5 个数据包大小不同。在尝试分配空间并将其放入 P 之前,请先尝试查看是否可以读取内容并将其写入控制台,因为我认为您的 fscanf 也是错误的(如果您的文件是 : 分隔的)。

标签: c file data-structures


【解决方案1】:

您面临的最大挑战是为工作选择正确的工具。您还必须将您的结构声明与您拥有的数据更好地匹配。

虽然scanf 是一个很好的工具,但当您需要阅读不同的输入行时,它就不是适合这项工作的工具了。在您的数据中,您有blank linesvarying number of fields 每行可供阅读。与其尝试 shoehorn 一系列测试和格式化字符串以便 scanf 可以工作,正确的方法是从文件中读取整行数据,然后解析该数据以获得所需的信息.

面向行输入的合适工具是fgetsgetline。在这种情况下,我更喜欢getline,因为它返回它在每行中读取的实际字符数,从而可以对空行/短行进行简单测试。

一旦您读取了一行数据,适合这项工作的工具是strtokstrsep。您还可以使用简单的指针手动解析数据行。无论如何,您解析数据并将分离的值分配给您的结构。

我已在下面注释掉了您的部分代码,以便您可以跟进更改。一些简单的风格,其余的实现我上面描述的。请注意,有很多方法可以做到这一点。仔细看一下,如果您有任何问题,请告诉我。具体了解为什么您不需要需要在结构中声明数组:

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

#define MAXS 500

// typedef struct __Packet {
//     int Source[500];
//     int Destination[500];
//     int Type[500];
//     int Port[500];
//     char Data[60];
// } Packet;

typedef struct {    /* don't use initial 'C'aps - c isn't c++ or visual basic */
    int src;        /* obviously, you are free to do so, but it's ugly...     */
    int dest;
    int type;
    int port;
    char *data;
} packet;

int main () {

    // banner ();           //call on banner
    // int exists (const char *filename);
    // const char s[2] = ":";
    // char filename[500];
    // char *token;
    // int n;
    // struct Packet;
    // Packet *P;

    char *filename = NULL;
    FILE *iFile = NULL;
    // FILE *oFile = NULL;
    char *line = NULL;      /* NULL forces getline to allocate  */
    size_t n = 0;           /* max chars to read (0 - no limit  */
    ssize_t nchr = 0;       /* number of chars actually read    */
    size_t idx = 0;         /* packet array index               */
    char *p = NULL;         /* general pointer to parse line    */
    char *sp = NULL;        /* pointer to save start address    */
    int i = 0;              /* general iterator                 */

    /* allocate an array of pointers to struct */
    packet **pkt = calloc (MAXS, sizeof (*pkt));
    if (!pkt) {
        fprintf (stderr, "error: allocation failed (**pkt)\n");
        exit (EXIT_FAILURE);
    }

    printf ("\n Input the file name: ");
    scanf ("%m[^\n]%*c", &filename);        /* older scanf versions use 'a' instead of 'm' */
    printf ("\n");

    iFile = fopen (filename, "r");
    if (!iFile) {
        fprintf (stderr, "error: unable to open file '%s'\n", filename);
        exit (EXIT_FAILURE);
    }

    /* read each line in iFile  */
    while ((nchr = getline (&line, &n, iFile)) != -1)
    {
        if (nchr < 4)               /* if blank or short line, skip     */
            continue;

        if (line[nchr-1] == '\n')   /* strip newline from end           */
            line[--nchr] = 0;   

        sp = line;                  /* save start address for getline   */

        pkt[idx] = calloc (1, sizeof (**pkt));  /* allocate structure   */
        if (!pkt[idx]) {
            fprintf (stderr, "error: allocation failed (pkt[%zd])\n", idx);
            exit (EXIT_FAILURE);
        }

        /* parse line and fill struct */
        if ((pkt[idx]->src = atoi (strtok (line, ":"))))
        {
            pkt[idx]->dest = atoi (strtok (NULL, ":"));
            pkt[idx]->type = atoi (strtok (NULL, ":"));
            pkt[idx]->port = atoi (strtok (NULL, ":"));
            if ((p = strtok (NULL, ":")))
                pkt[idx]->data = strdup (p);
        }

        idx++;                      /* increment pkt array index        */
        line = sp;                  /* restore start address of line    */
    }

    if (line) free (line);          /* free buffer allocated by getline */
    if (iFile) fclose (iFile);      /* close file stream when done      */
    if (filename) free (filename);  /* free memory allocate by scanf    */

    /* print array (you can also use 'for (i=0; i<idx; i++)' to iterate)*/
    printf (" array of struct content:\n\n");
    while (pkt[i])
    {
        printf (" pkt[%d]    src: %4d    dest: %4d    type: %4d    port: %4d    data: %s\n",
                i, pkt[i]->src, pkt[i]->dest, pkt[i]->type, pkt[i]->port, pkt[i]->data);
        i++;
    }

    i = 0;                          /* reset iterator variable to zero  */
    while (pkt[i])                  /* free all memory allocated        */
    {
        if (pkt[i]->data) free (pkt[i]->data);
        free (pkt[i]);
        i++;
    }
    if (pkt) free (pkt);

    printf ("\n");                  /* make it pretty   */

    return 0;
}

输入:

$ cat dat/structrd.txt
0001:0002:0003:0021:CLS
0001:0010:0003:0021:CLS
0001:0002:0002:0080:
0005:0002:0002:8080:

0005:0012:0002:8080:

输出:

$ ./bin/struct_rd_txt

 Input the file name: dat/structrd.txt

 array of struct content:

 pkt[0]    src:    1    dest:    2    type:    3    port:   21    data: CLS
 pkt[1]    src:    1    dest:   10    type:    3    port:   21    data: CLS
 pkt[2]    src:    1    dest:    2    type:    2    port:   80    data: (null)
 pkt[3]    src:    5    dest:    2    type:    2    port: 8080    data: (null)
 pkt[4]    src:    5    dest:   12    type:    2    port: 8080    data: (null)

注意:在使用破坏原始缓冲区的函数(如strtok)时,需要保存getline分配的缓冲区的起始地址,以便getline可以准确保留跟踪它正在使用的内存。 (如果你不:)


使用fgets的版本

如果您没有可用的getline,请使用fgets。那是另一个合适的工具。然后放慢速度,阅读man page,看看它是如何使用的。然后您可以正确更新程序。这将包括以下更改:

// size_t n = 0;           /* max chars to read (0 - no limit  */
...
line = calloc (MAXS, sizeof (char));
if (!line) {
    fprintf (stderr, "error: allocation of line failed.\n");
    exit (EXIT_FAILURE);
}

/* read each line in iFile  */
// while ((nchr = getline (&line, &n, iFile)) != -1)
while ((fgets (line, MAXS, iFile)) != NULL)
{
    nchr = strlen (line);

【讨论】:

  • 嗨,非常感谢您非常有帮助和详细的回答,您提供了很多帮助,我明天会坐下来尝试完成这项工作,哈哈。再次感谢您的努力,非常感谢他们
  • 很高兴我能帮上忙。花点时间浏览代码。新 C 程序员犯的#1 错误是认为他们可以略​​读参考手册或教程并开始编写代码。 C是一种精确的语言。这是它的一大优势。但是要真正使用它,您必须了解每行的每个部分的作用。如果您不理解某行的一部分,请查看man page 或c 参考手册。慢慢来,给自己时间消化细节,你会做得很好。最后提示。始终在 启用警告 -Wall -Wextra 的情况下进行编译,这将指出要修复的错误。
  • 嗨,我在 /* 读取 iFile 中的每一行 */ 时遇到编译器错误,而 ((nchr = getline (&line, &n, iFile)) != -1) 它说有一个互联网研究中对“getline”的未定义引用显然与编译器有关?
  • 嗯?你用的是什么编译器? getline 在 POSIX.1-2008 中标准化。你在使用一些奇怪的不符合标准的编译器吗?不用担心,只需使用 fgets。我会放弃更新。
  • 看看Version using fgets下最后的变化。
【解决方案2】:

我发现的一些错误是:

  • while(P &gt;=sizeof(Packet))这个检查似乎完全没有意义。请查看EOF 常量以确定何时结束循环。
  • (struct Packet*) 不要在这里使用关键字struct,因为Packet 已经是结构的typedef。
  • calloc(5, sizeof(Packet *))您不想分配指针的大小,而是分配实际结构的大小。
  • &amp;P-&gt;Source, &amp;P-&gt;Destination, ... 您不必传递数组的地址 (&amp;),因为数组本身一个指针。
  • fprintf(inputFile,"%s %d %d %d %d\n" %s 说明符应该是最后一个,因为第一个是 int 数组。此外,您必须取消引用 int 数组才能打印数字。

我认为您应该更详细地研究数组和指针。

【讨论】:

  • 您好,感谢您的帮助,我已应用您提到的更改
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多