我有一个包含空格分隔值的输入文件

AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B

我希望第一行的内容分别移到s1s2n
接下来的n行包含2个空格分隔的字符串,这些字符串将移动到
分别为production_leftproduction_right
布局也将在后续的行块中重复。样本数据有两个输入块。
我的代码如下
int main()
{
    char *s1[20], *t1[20];
    char *production_left[20], *production_right[20];
    int n;
    FILE *fp;
    fp = fopen("suffix.txt","r");
    int iss=0;
    do{
         fscanf(fp,"%s %s %d",s1[iss],t1[iss],&n);
         int i=0;
         while(i<n)
         {
             fscanf(fp,"%s %s",production_left[i],production_right[i]);
             i++;
         }
    }while(!eof(fp));
}

每次它都会给分割带来错误。

最佳答案

很多C语言,只是在你的头脑中保持清晰,你需要什么样的数据在你的代码的什么范围(块)中可用。在您的例子中,您正在使用的数据是数据文件每个部分的第一行(或标题)。为此,您有s1t1,但您没有什么可以保留的n,因此它可以与您的数据一起重用。由于n包含每个标题下预期的production_leftproduction_right索引数,只需创建一个索引数组,例如int idx[MAXC] = {0};来存储与每个ns1相关联的每个t1。这样您就可以保留该值,以便以后在迭代中使用。(MAXC只是为20定义的常量,以防止在代码中使用幻数)
接下来,您需要了解指针声明及其使用。char *s1[MAXC], *t1[MAXC], *production_left[MAXC], *production_right[MAXC];20s1t1production_left声明4个指针数组(production_right每个指针)。指针未初始化和未分配。虽然每一个都可以初始化为指针值,但是没有任何存储(分配的内存)与允许复制数据的指针值相关联。不能简单地使用fscanf并为每个值分配相同的指针值(它们都指向最后一个值——如果它仍在作用域中的话)
因此,您有两个选择:(1)使用2D数组,或(2)为每个字符串分配存储空间,并将字符串复制到新的内存块,并将指向该块开头的指针分配给(例如s1[x])。strdup函数在单个函数调用中提供分配和复制。如果没有strdup,则使用strlenmallocmemcopy编写是一个简单的函数(如果字符串有重叠的可能,则使用memmove)。
一旦确定了需要保留哪些值以供以后在代码中使用,就可以确保声明的变量的作用域正确,并且确保每个变量都已正确初始化并分配了存储空间,剩下的就是编写逻辑以使事情按您的意愿工作。
在转向示例之前,值得注意的是,您对数据的面向行的输入感兴趣。scanf系列提供格式化输入,但通常最好对实际输入使用面向行的函数(例如fgets),然后使用单独的解析,例如sscanf。在这种情况下,这很大程度上是一种清洗,因为第一个值是字符串值,%s格式说明符将跳过中间的空白,但通常情况并非如此。例如,你正在有效地阅读:

    char tmp1[MAXC] = "", tmp2[MAXC] = "";
    ...
    if (fscanf (fp, "%s %s %d", tmp1, tmp2, &n) != 3)
        break;

它可以很容易地替换为更强大的一点:
    char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
    ...
    if (!fgets (buf, sizeof buf, fp) ||
        sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
        break;

(这将分别验证读取和转换)
上面还注意到临时缓冲区tmp1tmp2(和buf用于fgets)的使用它通常有利于将输入读入临时值,这些值可以在最终存储之前验证,以便以后使用。
剩下的就是简单地按照正确的顺序把各个部分组合起来,以实现你的目标。下面是一个例子,它读取从第一个参数给出的文件名中的数据(或者如果没有给出文件名,则从stdin),然后输出数据并释放在退出之前分配的所有内存。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXC 20

int main (int argc, char **argv) {

    char *s1[MAXC], *t1[MAXC],
        *production_left[MAXC], *production_right[MAXC];
    int idx[MAXC] = {0},              /* storage for 'n' values */
        iss = 0, ipp = 0, pidx = 0;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* loop until no header row read, protecting array bounds */
    for (; ipp < MAXC && iss < MAXC; iss++) {
        char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
        int n = 0;

        if (!fgets (buf, sizeof buf, fp) ||
            sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
            break;

        idx[iss] = n;
        s1[iss] = strdup (tmp1);    /* strdup - allocate & copy */
        t1[iss] = strdup (tmp2);

        if (!s1[iss] || !t1[iss]) { /* if either is NULL, handle error */
            fprintf (stderr, "error: s1 or s1 empty/NULL, iss: %d\n", iss);
            return 1;
        }

        /* read 'n' data lines from file, protecting array bounds */
        for (int i = 0; i < n && ipp < MAXC; i++, ipp++) {
            char ptmp1[MAXC] = "", ptmp2[MAXC] = "";

            if (!fgets (buf, sizeof buf, fp) ||
                sscanf (buf, "%s %s", ptmp1, ptmp2) != 2) {
                fprintf (stderr, "error: read failure, ipp: %d\n", iss);
                return 1;
            }
            production_left[ipp] = strdup (ptmp1);
            production_right[ipp] = strdup (ptmp2);

            if (!production_left[ipp] || !production_right[ipp]) {
                fprintf (stderr, "error: production_left or "
                        "production_right empty/NULL, iss: %d\n", iss);
                return 1;
            }
        }
    }

    if (fp != stdin) fclose (fp);     /* close file if not stdin */

    for (int i = 0; i < iss; i++) {
        printf ("%-8s %-8s  %2d\n", s1[i], t1[i], idx[i]);
        free (s1[i]);  /* free s & t allocations */
        free (t1[i]);
        for (int j = pidx; j < pidx + idx[i]; j++) {
            printf ("  %-8s %-8s\n", production_left[j], production_right[j]);
            free (production_left[j]);  /* free production allocations */
            free (production_right[j]);
        }
        pidx += idx[i];  /* increment previous index value */
    }

    return 0;
}

示例输入文件
$ cat dat/production.txt
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B

示例使用/输出
$ ./bin/production <dat/production.txt
AA       BB         4
  A        B
  AB       BA
  AA       CC
  CC       BB
A        B          3
  A        C
  B        C
  c        B

内存使用/错误检查
在动态分配内存的任何代码中,对于任何分配的内存块,您都有两个职责:(1)始终保留指向内存块起始地址的指针,以便(2)在不再需要时可以释放它。
必须使用内存错误检查程序,以确保您不会尝试写入超出或超出已分配内存块的界限,尝试读取未初始化值或将条件跳转基于未初始化值,最后确认您释放了已分配的所有内存。
对于Linuxvalgrind是正常的选择。每个平台都有类似的内存检查程序。它们都很容易使用,只要运行你的程序就可以了。
$ valgrind ./bin/production <dat/production.txt
==3946== Memcheck, a memory error detector
==3946== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3946== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==3946== Command: ./bin/production
==3946==
AA       BB         4
  A        B
  AB       BA
  AA       CC
  CC       BB
A        B          3
  A        C
  B        C
  c        B
==3946==
==3946== HEAP SUMMARY:
==3946==     in use at exit: 0 bytes in 0 blocks
==3946==   total heap usage: 18 allocs, 18 frees, 44 bytes allocated
==3946==
==3946== All heap blocks were freed -- no leaks are possible
==3946==
==3946== For counts of detected and suppressed errors, rerun with: -v
==3946== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放所有已分配的内存,并且没有内存错误。
再看一遍,如果你还有问题,请告诉我。

08-16 16:21