如何改进我的代码以从文本表填充数组?

问题描述 投票:2回答:3

我是心理健康诊所的研究助理,我正在尝试编写一个C程序,将原始分数(在四个分测试中)转换为T分数。 C,无论好坏,都是我最熟悉的语言,但是因为我写了任何东西已经有一段时间了,所以请原谅我的无知。

每个子测试的每个原始分数仅对应于一个T分数。然而,反过来并不成立:每个T分数可以对应于多个原始分数(特别是在比例的任何极端),并且一些T分数不对应于任何原始分数。

我在文本文件中表示此数据的问题的解决方案是表示八列中的数据,每个奇数列(索引[0],[2],[4]和[6])表示T得分和每个甚至列表示与其对应的原始分数,这似乎现在解决了这个问题。如果T分数没有相应的原始分数,我已插入-1以保持fscanf满意。

但是,我希望你可以帮我找一个更聪明的解决方案。我也想知道是否有更好的数据结构可以让我智能地表示和操纵T分数和原始分数之间的不对称关系,也许这样每个T分数和一系列可能的值都会有一个条目。可以产生T分数的分测验?该文件如下所示:

20,0,20,0,20,-1,20,0
20,1,20,1,21,-1,20,1
20,2,20,2,22,0,20,2
20,3,20,3,23,1,20,3
20,4,20,4,24,2,20,4
20,5,20,5,25,3,20,5
20,6,20,6,26,4,20,6
20,7,20,7,27,5,20,7
20,8,20,8,28,6,20,8
20,9,20,9,29,7,20,9

你可以看到我到目前为止的内容。它执行以下操作:

  1. 使用for循环和二维整数数组,它从文件中检索数据并将其输入到数组中。 该表有83行,所以我将循环限制在83次迭代,但我确信必须有更好的方法来执行此操作,因此我不必将其硬编码到程序中(我想的可能是一段时间)当检测到某个数字(如-2)时停止的循环?)。 -1技巧似乎现在有效,但我也希望有更好的方法来处理表输入,以便我可以按原样使用它。但我真的不太了解fscanf的语法,以便更好地实现更好的东西。你能给我任何指示吗?
  2. 进一步使用for循环,它会遍历原始分数行并检查用户输入的分数与其在表格中的位置之间的身份。我想知道是否有一种聪明的方法将这些操作嵌套到上级for循环中?
  3. 当数字匹配时,分配T分数并打印结果。

这是代码:

#include <stdio.h>

int main(int argc, char *argv[]) {
  int tab[8][83];
  int r_v, r_s, r_bd, r_mr;
  int t_v, t_s, t_bd, t_mr;
  int i;
  FILE *input;

  input = fopen(argv[1], "r");

  printf("Subtest A score?\n");
  scanf("%i", &r_v);

  printf("Subtest B score?\n");
  scanf("%i", &r_s);

  printf("Subtest C score?\n");
  scanf("%i", &r_bd);

  printf("Subtest D score?\n");
  scanf("%i", &r_mr);

  for (i = 0; i < 83; i++) {
    fscanf(input, "%i,%i,%i,%i,%i,%i,%i,%i", &tab[0][i], &tab[1][i], &tab[2][i], &tab[3][i], &tab[4][i], &tab[5][i], &tab[6][i], &tab[7][i]);
    }

  for (i = 0; i < 83; i++) {
    if (r_v == tab[1][i]) {
      t_v = tab[0][i];
    }
  }

  for (i = 0; i < 83; i++) {
    if (r_s == tab[3][i]) {
      t_s = tab[2][i];
    }
  }

  for (i = 0; i < 83; i++) {
    if (r_bd == tab[5][i]) {
      t_bd = tab[4][i];
    }
  }

  for (i = 0; i < 83; i++) {
    if (r_mr == tab[7][i]) {
      t_mr = tab[6][i];
    }
  }

  printf("The participant had the following raw scores: %d, %d, %d, and %d.\n", r_v, r_s, r_bd, r_mr);
  printf("Which corresponds to the following T scores: %d, %d, %d, and %d.\n", t_v, t_s, t_bd, t_mr);

  return 0;
}
c arrays data-structures io
3个回答
2
投票

错误处理

首先,发布的代码缺少错误检查。用户可能无法提供文件名,或者可能提供不存在的文件名。该文件可能无法打开。用户可以输入非数字输入。表中可能找不到输入值。

无法提供文件名可以通过在程序开头检查argc来捕获。无法打开文件可以通过检查fopen()返回的值并在返回空指针时退出消息来处理。

如果在表中未找到输入值,则不会在关联变量中存储任何值。处理此问题的一种方法是将关联变量初始化为表中不期望的标记值。我在下面的代码中选择了-1

要处理用户输入,可以添加一个get_integer()函数,它将输入提示消息作为参数并返回输入数字。如果用户输入格式错误的输入,该函数将继续提示输入,直到给出正确的输入。此函数还可以验证输入是否为非负整数,以防止根据-1的用户输入值分配值。

一个改进可能是在用消息退出程序之前为用户提供错误输入的次数设置上限。

数据结构

我不建议将表存储为2d数组并通过索引访问表的各个字段,而是建议为表的行创建一个结构,以及这些table[]s的struct数组。这将允许描述性字段名称并使代码更清晰。

阅读文件

我建议使用fgets()从文件中获取行。当达到文件结尾时,此函数返回空指针,允许使用while循环进行控制。 sscanf()函数可用于解析每一行并将值存储在适当的位置。与返回有意义值的所有函数一样,应检查sscanf()返回的值以确保输入符合预期。如果不是,则可以通过退出程序并显示错误消息来处理该情况。在考虑更多用户输入之前,我还会考虑从文件构建表。

当每条线成功添加到表中时,可以递增line计数器以跟踪table[]数组中的行数。

搜索表

这里只需要一个循环。 line计数器可用于确定要检查的行数的上限。由于OP建议每个搜索值都是唯一的,一旦找到并存储了相关变量,关联变量就不会再次更改。如果在表中未找到搜索值,则关联变量将继续保持初始值(-1)。

一个改进是添加一个标志来指示何时找到所有搜索值,并在发生这种情况时提前退出循环。

这是一个示例程序:

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

#define MAX_ROWS  100
#define BUF_SZ    1024

struct Record
{
    int t_v;
    int r_v;
    int t_s;
    int r_s;
    int t_bd;
    int r_bd;
    int t_mr;
    int r_mr;
};

int get_integer(const char *prompt);

int main(int argc, char *argv[])
{
    struct Record table[MAX_ROWS];
    int r_v, r_s, r_bd, r_mr;
    int t_v = -1;
    int t_s = -1;
    int t_bd = -1;
    int t_mr = -1;
    FILE *input;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s filename\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    input = fopen(argv[1], "r");

    if (input == NULL) {
        fprintf(stderr, "Unable to open file: %s\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    /* Read table  into array */
    int line = 0;
    char buffer[BUF_SZ];
    while (fgets(buffer, sizeof buffer, input) != NULL) {
        if (sscanf(buffer, "%i,%i,%i,%i,%i,%i,%i,%i",
                   &table[line].t_v,
                   &table[line].r_v,
                   &table[line].t_s,
                   &table[line].r_s,
                   &table[line].t_bd,
                   &table[line].r_bd,
                   &table[line].t_mr,
                   &table[line].r_mr)
            != 8) {
            fprintf(stderr, "Format error in line %d\n", line);
            exit(EXIT_FAILURE);
        }
        ++line;
    }
    fclose(input);

    r_v = get_integer("Subtest A score?\n");
    r_s = get_integer("Subtest B score?\n");
    r_bd = get_integer("Subtest C score?\n");
    r_mr = get_integer("Subtest D score?\n");

    for (int i = 0; i < line; i++) {
        if (r_v == table[i].r_v) {
            t_v = table[i].t_v;
        }
        if (r_s == table[i].r_s) {
            t_s = table[i].t_s;
        }
        if (r_bd == table[i].r_bd) {
            t_bd = table[i].t_bd;
        }
        if (r_mr == table[i].r_mr) {
            t_mr = table[i].t_mr;
        }
    }

    printf("The participant had the following raw scores: "
           "%d, %d, %d, and %d.\n",
           r_v, r_s, r_bd, r_mr);
    printf("Which corresponds to the following T scores: "
           "%d, %d, %d, and %d.\n",
           t_v, t_s, t_bd, t_mr);

    return 0;
}

int get_integer(const char *prompt)
{
    char buf[BUF_SZ];
    int ret;

    do {
        printf("%s", prompt);
        fflush(stdout);
        if (fgets(buf, sizeof buf, stdin) == NULL) {
            fprintf(stderr, "Input error in get_integer()\n");
            exit(EXIT_FAILURE);
        }
    } while (sscanf(buf, "%i", &ret) != 1 || ret < 0);

    return ret;
}

这是使用发布的输入文件的示例交互:

λ> ./tvals t_vals.dat
Subtest A score?
3
Subtest B score?
4
Subtest C score?
5
Subtest D score?
6
The participant had the following raw scores: 3, 4, 5, and 6.
Which corresponds to the following T scores: 20, 20, 27, and 20.

0
投票

嗯..我没有很好地得到那些代码。我可以看到,如果A得分是5,那么t_v是20(如果得分是0则)。我没有看到你将文件中的值加载到tab矩阵。所以tab也是单元化的。如果你想改进代码,可以使用Anothed。你做了5个循环..当你可以简单地将所有这些合并为一个。 Aforloop在顶部有fscanf并且满足您的所有条件。


0
投票
#include <stdio.h>
#include <stdlib.h>

#define NO_OF_ROW       100     //You can change if no of rows increases
#define EACH_LINE_SIZE  100

#define SUBTEST_A       1
#define SUBTEST_B       3
#define SUBTEST_C       5
#define SUBTEST_D       7

int main(int argc, char *argv[])
{
    int tab[8][NO_OF_ROW] = {{0}};
    int r_v, r_s, r_bd, r_mr;
    int t_v=0, t_s=0, t_bd=0, t_mr=0;
    int i;
    FILE *input;
    int no_rows =0;

    char* line = malloc(EACH_LINE_SIZE);

    input = fopen("User.txt", "r");

    if(NULL == input)
    {
        return 0;
    }

    printf("Subtest A score?\n");
    scanf("%i", &r_v);

    printf("Subtest B score?\n");
    scanf("%i", &r_s);

    printf("Subtest C score?\n");
    scanf("%i", &r_bd);

    printf("Subtest D score?\n");
    scanf("%i", &r_mr);

    while (fgets(line, EACH_LINE_SIZE, input) != NULL)
    {
        sscanf(line, "%d,%d,%d,%d,%d,%d,%d,%d", &tab[0][i], &tab[1][i], &tab[2][i], &tab[3][i], &tab[4][i], &tab[5][i], &tab[6][i], &tab[7][i]);
        //printf("%d,%d,%d,%d,%d,%d,%d,%d\n", tab[0][i], tab[1][i], tab[2][i], tab[3][i], tab[4][i], tab[5][i], tab[6][i], tab[7][i]);
        i++;
        if(NO_OF_ROW <= i)
        {
            printf("Stop file reading \n");
            break;          //if the file has more line -Just end it here
        }
    }

    no_rows = i;

    for(i=0 ; i < no_rows ; i++)
    {
        if (r_v == tab[SUBTEST_A][i])
        {
            t_v = tab[SUBTEST_A-1][i];
        }

        if (r_s == tab[SUBTEST_B][i])
        {
            t_s = tab[SUBTEST_B-1][i];
        }

        if (r_bd == tab[SUBTEST_C][i])
        {
            t_bd = tab[SUBTEST_C-1][i];
        }
        if (r_mr == tab[SUBTEST_D][i])
        {
            t_mr = tab[SUBTEST_D-1][i];
        }
    }

    printf("The participant had the following raw scores: %d, %d, %d, and %d.\n", r_v, r_s, r_bd, r_mr);
    printf("Which corresponds to the following T scores: %d, %d, %d, and %d.\n", t_v, t_s, t_bd, t_mr);

    free(line);

    return 0;
}
  1. 而不是常数83,尝试使用不同的方法,因此您可以改变行数
  2. 而不是4循环,试图适合单循环
© www.soinside.com 2019 - 2024. All rights reserved.