【问题标题】:Convert array size转换数组大小
【发布时间】:2016-06-18 00:50:59
【问题描述】:

以下代码从文件中获取输入并将其存储在一维数组中。我想要一个矩阵类型的输入,例如:

1,2,4
3,4,5
5,6,7

(或)

2,3,4,5
4,5,6,7
7,6,5,4
3,4,5,6

矩阵的大小不同,它们用逗号分隔,存储在二维数组中。我应该对以下代码进行哪些更改?

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

int main(){
    char file[51];
    int data, row, col, c, count, inc;
    int *array, capacity=50;
    char ch;
    array = (int*)malloc(sizeof(int) * capacity);
    printf("\nEnter the name of the file with its extention\n");
    scanf("%s", file);
    FILE *fp = fopen(file, "r"); 
    row = col = c = count = 0;
    while (EOF != (inc = fscanf(fp,"%d%c", &data, &ch)) && inc == 2){
        ++c; //COLUMN count
        if (capacity == count)
            array = (int*)realloc(array, sizeof(int) * (capacity *= 2));
        array[count++] = data;
        if(ch == '\n'){
            ++row;
            if (col == 0){
                col = c;
            } else if (col != c){
                fprintf(stderr, "format error of different Column of Row at %d\n", row);
                goto exit;
            }
            c = 0;
        } else if (ch != ',') {
            fprintf(stderr, "format error of different separator(%c) of Row at %d \n", ch, row);
            goto exit;
        }
    }
    {   //check print
        int i, j;
        //int (*matrix)[col] = array;
        for(i = 0; i < row; ++i){
            for(j = 0; j < col; ++j)
                printf("%d ", array[i * col + j]);//matrix[i][j]
            printf("\n");
        }
    }
exit:
    fclose(fp);
    free(array);
    return 0;
}

【问题讨论】:

  • 要做的更改之一:向内存分配和文件打开添加错误检查。
  • 谢谢你。注意到变化。
  • goto 指令应始终通过使用条件来避免。
  • @Olaf 是的,我知道。但是,由于goto 可能很容易出错,因此如果不是 100% 必要,则不应使用它。通常它不是并且可以避免。例如。在这种情况下,还可以编写一个函数cleanup(FILE* fp, int* array) 来释放分配的内存并关闭文件。

标签: c arrays algorithm multidimensional-array


【解决方案1】:

简单地保存和读取数组总是一个单维字符串,其长度是所有维度乘以类型大小的乘积。然后返回一个指向 void 的指针,该指针将分配给指向所需维度数组的指针。

void *MyReadFnc(char *filename, size_t size)
{
    char *p = malloc(size);
    //open file and load the data
    return (void *)p;
}
...
//Call the function to read in our array.
//For who don't see it we want assign to an array of 5 dimensions
//array[10][2][3][4][5]
//Note that the first dimension is omitted, but we have to use it in array size calcullation
int (*array)[2][3][4][5] = MyReadFnc("Filename.ext", 2*3*4*5*10*sizeof(int));
...
printf("element[9][1][2][3][4] = %d\n", array[9][1][2][3][4]);
...
//Again an array of 3 dimensions:
//array2[10][20][30]
int (*array2)[20][30] = MyReadFnc("Filename.ext", 20*30*10*sizeof(int));
...
printf("element[9][19][29] = %d\n", array[9][19][29]);

我们使用void * 作为输出,因为该例程是通用的并且可以处理任何数组(任意维数)。而void * 的使用只是C 语言处理此类杂乱类型的标准且自然的方式。这就是 C 语言的工作方式。如果您想要更强大的类型检查,您应该更改语言。
以下是用于读取数组的更通用的代码。

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

//Because we read whole data as single stream like a 1 dimension array, totElems
//is the total number of elements expressed as the product of all dimensions of
//the final array
void *ReadArray(char *fname, size_t totElems, size_t ElemSize)
{
    FILE *fp = fopen(fname, "r");
    if (!fp)
    {
        fprintf(stderr, "Can't open file <%s>\n", fname);
        exit(-1);
    }

    int *p = calloc(totElems, ElemSize);
    if (!p)
    {
        fprintf(stderr, "Can't allocate memory\n");
        exit(-1);
    }

    for (size_t i=0; ; i++)
    {
        int a;

        if (EOF==fscanf(fp, "%d,", &a))
            break;
        p[i] = a;
    }

    fclose(fp);
    return (void *)p;
}

int main(void)
{
    char file[51];
    printf("\nEnter the name of the file with its extention\n");
    scanf("%s", file);

    printf("Array 4x4\n");
    //First sample: declare a pointer to a 2 dimension array 4x4 elements
    int (*array)[4];
    array = ReadArray(file, 16, sizeof(int));
    for (int i=0; i<4; i++)
    {
        for(int j=0; j<4; j++)
            printf("%d,", array[i][j]);
        printf("\n");
    }

    printf("Array 2x8\n");
    //Second sample: declare a pointer to a 2 dimension array 2x8 elements
    int (*array2)[8];
    array2 = ReadArray(file, 16, sizeof(int));
    for (int i=0; i<2; i++)
    {
        for(int j=0; j<8; j++)
            printf("%d,", array2[i][j]);
        printf("\n");
    }

    printf("Array 2x2x4\n");
    //Third sample: declare a pointer to a 3 dimension array 2x2x4 elements
    int (*array3)[2][4];
    array3 = ReadArray(file, 16, sizeof(int));
    for (int i=0; i<2; i++)
    {
        for(int j=0; j<2; j++)
        {
            if(j) printf (" - ");
            for (int k=0; k<4; k++)
                printf("%d,", array3[i][j][k]);
        }
        printf("\n");
    }

    return 0;
}

您可以将结果分配给任何维度的数组;)。不需要强制转换,因为数组对象以void * 的形式返回,以实现最大的灵活性。还要注意,因为任何 C 数组都是数组数组等的数组,所以它实际上是独立于维度的。该函数返回的是一个数组,该数组可以符合任何关于 2 个声明的定义: 1. 该数组的类型与 ElemSize 中指定的 sizeof 相同(为了简单起见,这里硬连线到 int), 2.全维度乘积为&lt;=totElems.
这当然只是一个示例,作为开发更复杂版本的基础,一个好的开发可能是使其能够处理任何类型的数据(不仅仅是 int)。我相信这对于需要一个起点来提高自己和扩展创造力的初学者来说是一个很好的练习。
最后,只需考虑在符合 C99-C11 的编译器上,您可以编写: printf("数组 2x2x4 使用变量\n");

//Fourth sample: declare a pointer to a 3 dimension array 2x2x4 elements
//using variables for dimensions
int x = 2;
int y = 2;
int z = 4;
int (*array4)[y][z];
array4 = ReadArray(file, 16, sizeof(int));
for (int i=0; i<x; i++)
{
    for(int j=0; j<y; j++)
    {
        if(j) printf (" - ");
        for (int k=0; k<z; k++)
            printf("%d,", array4[i][j][k]);
    }
    printf("\n");
}

【讨论】:

  • 1) 不要使用幻数。它们是麻烦的保证 2) 直接的方法是始终使用指针指向的对象sizeofsizeof(*array):没有冗余,您不必考虑正确的类型(您仍然有 tgo乘以外部尺寸,当然)。
  • 参见维基百科:en.wikipedia.org/wiki/…
  • @Olaf 还是不明白。这些数字是用户数组的任意维度,而不是幻数。唯一引用与编译器相关的东西是sizeof(int) 周围没有悬空数字。
  • 这不是开玩笑。是的,它们是神奇的数字。链接的文章很清楚。我真的很想为使用真正的 n-D 数组提供 UV,但是在不需要的情况下破坏类型系统是一种不好的方法。为什么要使用容易出错的void *幻数 而不是宏常量和实际类型等缺陷?
  • @Olaf 再说一次Olaf,也许我很困惑,但是我不明白你指的神奇数字在哪里?。你能告诉我你到底是什么意思吗?
【解决方案2】:

好的,您当前的代码已经知道如何确定第一行末尾的列数并控制所有行的列数相同。

您可以这样在第一行的末尾分配一个二维数组:

capacity = cols * cols;
int (* arr)[cols] = malloc(sizeof(int) * capacity); // array is a 2D array cols x cols

(当然,你必须将第一行存储在另一个数组中,并且必须将这些数据复制到新分配的arr中......)

然后你这样分配:

arr[row][c] = data;

唯一的要求是您的编译器接受可变长度数组。它在 C99 中指定,但一些旧的 MS 编译器(至少到 2008 年)不接受它。

【讨论】:

    【解决方案3】:

    这使用 fgets 读取每一行并使用 strtol 解析一行中的整数。

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <limits.h>
    
    int get_int_range ( char *line, char **next, char *term, int *value, int min, int max);
    int get_int_series ( int cols, int dest[][cols], int inputrow, int min, int max, char *line, char *delim);
    int get_int_count ( int min, int max, char *line, char *delim);
    
    int main( int argc, char *argv[])
    {
        char line[900] = {'\0'};
        char file[100] = {'\0'};
        int valid = 0;
        int rows = 0;
        int cols = 0;
        int eachrow = 0;
        int eachcol = 0;
        FILE *fp = NULL;
    
        printf ( "Enter the name of the file with it's extension\n");
        fgets ( file, sizeof ( file), stdin);
        file[strcspn ( file, "\n")] = '\0';//remove trailing newline
    
        if ( ( fp = fopen ( file, "r")) != NULL) {
    
            fgets ( line, sizeof ( line), fp);//read a line
            rows = get_int_count ( INT_MIN, INT_MAX, line, ",\n");
            rewind ( fp);
            if ( rows) {
                cols = rows;
                //once the size is obtained, the array can be declared
                int array[rows][cols];
    
                for(eachrow = 0; eachrow < rows; eachrow++) {
                    if ( ( fgets ( line, sizeof ( line), fp)) == NULL) {//read a line
                        fclose ( fp);
                        printf ( "Problem! not enough lines in file\n");
                        return 1;
                    }
                    valid = get_int_series ( cols, array, eachrow, INT_MIN, INT_MAX, line, ", \n");
                    if ( !valid) {
                        fclose ( fp);
                        printf ( "Problem!\n");
                        return 1;
                    }
                }
                if ( ( fgets ( line, sizeof ( line), fp)) != NULL) {//read a line
                    fclose ( fp);
                    printf ( "Problem! too many lines in file\n");
                    return 1;
                }
                for(eachrow = 0; eachrow < rows; eachrow++) {
                    for(eachcol = 0; eachcol < cols; eachcol++) {
                        printf("[%d] ", array[eachrow][eachcol]);
                    }
                    printf("\n");
                }
                printf("\nDone\n");
            }
            fclose ( fp);
        }
        return 0;
    }
    
    int get_int_range ( char *line, char **next, char *term, int *value, int min, int max)
    {
        long int input = 0;
        char *end = NULL;
    
        errno = 0;
        input = strtol ( line, &end, 10);//get the integer from the line
        if ( end == line) {
            printf ( "input MUST be a number\n");
            return 0;
        }
        if ( *end != '\0' && ( strchr ( term, *end) == NULL)) {
            printf ( "problem with input: [%s] \n", line);
            return 0;
        }
        if ( ( errno == ERANGE && ( input == LONG_MAX || input == LONG_MIN))
        || ( errno != 0 && input == 0)){
            perror ( "input");
            return 0;
        }
        if ( input < min || input > max) {
            printf ( "input out of range %d to %d\n", min, max);
            return 0;
        }
    
        if ( next != NULL) {
            *next = end;
        }
        *value = input;//set the value
        return 1;
    }
    
    int get_int_series ( int cols, int dest[][cols], int inputrow, int min, int max, char *line, char *delim)
    {
        char *end = NULL;
        char *each = NULL;
        int valid = 0;
        int input = 0;
        int count = 0;
        int temp[cols];
    
        each = line;
        do {
            valid = get_int_range ( each, &end, delim, &input, INT_MIN, INT_MAX);
            if ( !valid) {
                printf ( "input MUST be a number\n");
                return 0;
            }
            if ( valid) {
                temp[count] = input;
                count++;
                if ( count > cols) {
                    printf ( "too many integers. %d entered. only enter %d\n", count, cols);
                    return 0;
                }
            }
            while ( *end && strchr ( delim, *end)) {//skip any number of delimitors
                end++;
            }
            each = end;
        } while ( end && *end);
    
        if ( count < cols) {
            printf ( "too few integers. need %d entered. only entered %d\n", cols, count);
            return 0;
        }
        while ( count) {
            count--;
            dest[inputrow][count] = temp[count];//set the value
        }
        return 1;
    }
    
    int get_int_count ( int min, int max, char *line, char *delim)
    {
        char *end = NULL;
        char *each = NULL;
        int valid = 0;
        int input = 0;
        int count = 0;
    
        each = line;
        do {
            valid = get_int_range ( each, &end, delim, &input, INT_MIN, INT_MAX);
            if ( !valid) {
                return count;
            }
            if ( valid) {
                count++;
            }
            while ( *end && strchr ( delim, *end)) {//skip any number of delimitors
                end++;
            }
            each = end;
        } while ( end && *end);
    
        return count;
    }
    

    【讨论】:

      猜你喜欢
      • 2019-05-27
      • 2011-03-10
      • 2013-02-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-22
      • 2019-09-14
      • 1970-01-01
      • 2022-11-02
      相关资源
      最近更新 更多