读取文件时输出错误
wrong output while reading file
我正在尝试将文件的内容读入矩阵。因为我将有几个行和列数量未知的文件,所以我为矩阵动态分配了内存。
到目前为止我的代码。
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char** map, chr;
int column = 0, row = 0, columns = 0, rows = 0, total_elements = 0;
FILE* file = fopen("file.txt", "r+");
// count numbers of rows and columns
while (chr != EOF)
{
if (chr == '\n')
{
rows = rows + 1;
}
chr = getc(file);
total_elements+=1;
}
rows += 1;
//Dividing the total number of elements by the number of rows to find the number of columns
columns = (total_elements/rows) - 1;
// alocate space for matrix
map = (char **) malloc(rows * sizeof(char *));
// allocating space for each column of each row
for (row = 0; row < rows; row++) {
map[row] = (char *) malloc(columns * sizeof(char));
}
// file reading
for (row = 0; row < rows; row++) {
for (column = 0; column < columns; column++) {
if (!fscanf(file, "%c", &map[row][column]))
break;
printf("%c", map[row][column]);
}
printf("\n");
}
fclose(file);
free(map);
return 0;
这是文件:
....*.....................
..........................
........*........*........
.....*....................
...............*....*.....
..*.......*...............
............*.............
..........................
..............*...........
..................*.......
..*.......*......*........
....*..*..................
...**.....................
..........*...............
....................*.....
..........................
....**....................
......................*...
预期的输出是文件的内容,但我得到的输出格式错误。
?Å É/Å @Q▓v @Q
?Å É/Å xA¢v
?Å É/Å ÊÐ C Å └ Å µ
?Å É/Å
?Å É/Å
?Å É/Å T(Å P(
?Å É/Å @Q▓v @Q
?Å É/Å xA¢vx(
?Å É/Å ╩ ╩ÈÐ └ Å É&Å ug
?Å É/Å ┼ ┼═Ð └ Å É&Å ├
?Å É/Å
?Å É/Å H
?Å É/Å
?Å É/Å
?Å É/Å
░Å É/Å
ä
H
我真的不知道我在哪里犯了这个错误。
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char **map, chr;
int column = 0, row = 0, columns = 0, rows = 0, total_elements = 0;
FILE *file = fopen("file.txt", "r+");
// count numbers of rows and columns
while (1) {
chr = (char) getc(file);
if (chr == EOF)
break;
if (chr == '\n') {
rows = rows + 1;
} else {
//\n is not part of matrix
total_elements += 1;
}
}
rows += 1;
printf("%d,%d\n", total_elements, rows); //no need -1 since did not counted \n
//Dividing the total number of elements by the number of rows to find the number of columns
columns = (total_elements / rows);
// alocate space for matrix
map = (char **) malloc(rows * sizeof(char *));
// allocating space for each column of each row
for (row = 0; row < rows; row++) {
map[row] = (char *) malloc(columns * sizeof(char));
}
//fix offset
rewind(file);
// file reading
for (row = 0; row < rows; row++) {
for (column = 0; column < columns; column++) {
chr = (char) getc(file);
map[row][column] = chr;
}
chr = (char) getc(file); //read \n but not write to array
}
row = 0;
for (row = 0; row < rows; row++) {
for (column = 0; column < columns; column++) {
printf("%c", map[row][column]);
}
printf("\n");
}
fclose(file);
free(map);
return 0;
}
至于你读取的文件类型和分配内存的方式,没有必要将整个文件读取两次:
.....*....................
...............*....*.....
..*.......*...............
............*.............
数据是面向行的,而且 --- 我想 --- 所有行都具有相同的列数。数据将存储在
char **map;
一个指针数组,所以每个map[i]
是char*
并且可以容纳一行数据。
fscanf()
是为使用表格数据而编写的,可能带有分隔符和不同的字段类型,例如 csv 文件包含许多 int
和 float
数据以 #
分隔。使用 fscanf()
读取字符没有多大意义,您可以使用 fgetc()
或 fread()
代替。
关于您的代码
- 如前所述,您需要
rewind
文件才能再次读取数据
- 另外,如前所述,您需要测试
'\n'
,因为它不是数据
- 始终根据说明符的数量测试
fscanf()
return
考虑到我上面所说的,下面的循环会消耗你的文件
rewind(file);
row = 0;
column = 0;
while (1 == fscanf(file, "%c", &map[row][column]))
{
if (map[row][column] == '\n')
{ row += 1, column = 0;
printf("\n");
}
else
{ printf("%c", map[row][column]);
column += 1;
}
}; // while();
fclose(file);
一般来说,在平坦的内存区域中使用地图数据更易于管理,仅使用 char*
,以 C 方式,逐行存储地图,而不是使用 char**
更灵活的方式
使用 char**
方式,您可以按行读取数据并将地图保留为字符串,因为它可能对显示和使用有用,同时仍然使用
map[][]
供参考。
我给你举个例子
此外,既然你说你有很多文件大小,你应该考虑将文件名作为参数传递
要获得更多控制,您可以使用一些封装并将数据放在 struct
中,例如
typedef struct
{
int cols;
int rows;
int size;
char** map;
} Grid;
这样你就可以拥有像
这样的功能
int show_grid(Grid*,const char*);
并写
char title[100];
sprintf(title, "\n Map for %s\n",file_name);
show_grid(&gr, title);
在屏幕上看到
Map for file.txt
[18 rows, 26 columns]
....*.....................
..........................
........*........*........
.....*....................
...............*....*.....
..*.......*...............
............*.............
..........................
..............*...........
..................*.......
..*.......*......*........
....*..*..................
...**.....................
..........*...............
....................*.....
..........................
....**....................
......................*...
而且代码可以简单到4行:
void show_grid(Grid* g, const char* msg)
{
if (msg != NULL) printf("%s\n", msg);
printf("[%d rows, %d columns]\n",
g->rows, g->cols);
for (int i = 0; i < g->rows; i+=1)
printf("%s\n", g->map[i]);
printf("\n");
}
确定列大小
int ch = 0;
for (gr.cols = 0; ch != '\n'; gr.cols += 1)
{
ch = fgetc(F);
if (feof(F)) return -2;
}
您只需要找到第一行的结尾,因为所有行的大小都相同,并且内存是按行分配的。
在使用char*
和平坦区域的情况下,分配与文件一样大的区域可能更简单,使用stat
或ftell
来获取文件大小而不是读取文件两次。
按行块分配内存
使用这种方式速度更快,使用fgets()
读取数据每次调用消耗一整行。此外,由于数据无论如何都不是平坦的,您可以将线条保留为字符串以简化显示。见:
// blocksize in lines
int row = 0;
while (!feof(F))
{
gr.map[row] = (char*)malloc(gr.cols); // new row
fgets(gr.map[row], gr.cols, F);
gr.map[row][gr.cols - 2] = 0;
row += 1;
if (row == gr.size)
{ // expand block
int new_size = gr.size + BLKSIZE;
char** temp = (char**)realloc(gr.map, sizeof(char*)*new_size);
if (temp == NULL) break;
gr.map = temp;
gr.size = new_size;
};
};
fclose(F);
你可以有一个合适的BLKSIZE
不调整多次
在命令行传递文件名
这样你可以有一个默认值,但也可以在命令行中传递文件名,以便它可以在脚本中使用。由于显示功能方便地显示文件名,您可以轻松检查任意数量的文件,如
// open file as 'F'
const char* default_file_name = "file.txt";
char file_name[80];
FILE* F = NULL;
if (argc > 1)
strcpy(file_name, argv[1]);
else
strcpy(file_name, default_file_name);
F = fopen(file_name, "r");
if (F == NULL)
{
perror("Could not open file");
return -1;
}
例子
SO> .\f0-0922
Map for file.txt
[18 rows, 26 columns]
....*.....................
..........................
........*........*........
.....*....................
...............*....*.....
..*.......*...............
............*.............
..........................
..............*...........
..................*.......
..*.......*......*........
....*..*..................
...**.....................
..........*...............
....................*.....
..........................
....**....................
......................*...
SO> .\f0-0922 other.txt
Map for other.txt
[5 rows, 5 columns]
....*
...*.
..*..
.*...
*....
考试代码
#define BLKSIZE 20
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int cols;
int rows;
int size;
char** map;
} Grid;
void show_grid(Grid*,const char*);
void free_grid(Grid*);
int main(int argc, char** argv)
{
// open file as 'F'
const char* default_file_name = "file.txt";
char file_name[80];
FILE* F = NULL;
if (argc > 1)
strcpy(file_name, argv[1]);
else
strcpy(file_name, default_file_name);
F = fopen(file_name, "r");
if (F == NULL)
{
perror("Could not open file");
return -1;
}
// define grid
Grid gr = {0, 0, 0, NULL};
// set 'cols' to column size
int ch = 0;
for (gr.cols = 0; ch != '\n'; gr.cols += 1)
{
ch = fgetc(F);
if (feof(F)) return -2;
}
gr.cols = gr.cols + 1; // add space to a terminating 0
rewind(F); // roll back 1st line
gr.map = (char**)malloc((BLKSIZE * gr.cols) * sizeof(char*)); // initial size
gr.size = BLKSIZE;
// blocksize in lines
int row = 0;
while (!feof(F))
{
gr.map[row] = (char*)malloc(gr.cols); // new row
fgets(gr.map[row], gr.cols, F);
gr.map[row][gr.cols - 2] = 0;
row += 1;
if (row == gr.size)
{ // expand block
int new_size = gr.size + BLKSIZE;
char** temp = (char**)realloc(gr.map, sizeof(char*)*new_size);
if (temp == NULL) break;
gr.map = temp;
gr.size = new_size;
};
};
fclose(F);
gr.rows = row;
gr.cols -= 2;
char title[100];
sprintf(title, "\n Map for %s\n",file_name);
show_grid(&gr, title);
free_grid(&gr);
return 0;
}
void show_grid(Grid* g, const char* msg)
{
if (msg != NULL) printf("%s\n", msg);
printf("[%d rows, %d columns]\n", g->rows, g->cols);
for (int i = 0; i < g->rows; i += 1) printf("%s\n", g->map[i]);
printf("\n");
}
void free_grid(Grid* g)
{
for (int i = 0; i < g->rows; i += 1) free(g->map[i]);
free(g->map);
g->map = NULL;
return;
}
我正在尝试将文件的内容读入矩阵。因为我将有几个行和列数量未知的文件,所以我为矩阵动态分配了内存。
到目前为止我的代码。
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char** map, chr;
int column = 0, row = 0, columns = 0, rows = 0, total_elements = 0;
FILE* file = fopen("file.txt", "r+");
// count numbers of rows and columns
while (chr != EOF)
{
if (chr == '\n')
{
rows = rows + 1;
}
chr = getc(file);
total_elements+=1;
}
rows += 1;
//Dividing the total number of elements by the number of rows to find the number of columns
columns = (total_elements/rows) - 1;
// alocate space for matrix
map = (char **) malloc(rows * sizeof(char *));
// allocating space for each column of each row
for (row = 0; row < rows; row++) {
map[row] = (char *) malloc(columns * sizeof(char));
}
// file reading
for (row = 0; row < rows; row++) {
for (column = 0; column < columns; column++) {
if (!fscanf(file, "%c", &map[row][column]))
break;
printf("%c", map[row][column]);
}
printf("\n");
}
fclose(file);
free(map);
return 0;
这是文件:
....*.....................
..........................
........*........*........
.....*....................
...............*....*.....
..*.......*...............
............*.............
..........................
..............*...........
..................*.......
..*.......*......*........
....*..*..................
...**.....................
..........*...............
....................*.....
..........................
....**....................
......................*...
预期的输出是文件的内容,但我得到的输出格式错误。
?Å É/Å @Q▓v @Q
?Å É/Å xA¢v
?Å É/Å ÊÐ C Å └ Å µ
?Å É/Å
?Å É/Å
?Å É/Å T(Å P(
?Å É/Å @Q▓v @Q
?Å É/Å xA¢vx(
?Å É/Å ╩ ╩ÈÐ └ Å É&Å ug
?Å É/Å ┼ ┼═Ð └ Å É&Å ├
?Å É/Å
?Å É/Å H
?Å É/Å
?Å É/Å
?Å É/Å
░Å É/Å
ä
H
我真的不知道我在哪里犯了这个错误。
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char **map, chr;
int column = 0, row = 0, columns = 0, rows = 0, total_elements = 0;
FILE *file = fopen("file.txt", "r+");
// count numbers of rows and columns
while (1) {
chr = (char) getc(file);
if (chr == EOF)
break;
if (chr == '\n') {
rows = rows + 1;
} else {
//\n is not part of matrix
total_elements += 1;
}
}
rows += 1;
printf("%d,%d\n", total_elements, rows); //no need -1 since did not counted \n
//Dividing the total number of elements by the number of rows to find the number of columns
columns = (total_elements / rows);
// alocate space for matrix
map = (char **) malloc(rows * sizeof(char *));
// allocating space for each column of each row
for (row = 0; row < rows; row++) {
map[row] = (char *) malloc(columns * sizeof(char));
}
//fix offset
rewind(file);
// file reading
for (row = 0; row < rows; row++) {
for (column = 0; column < columns; column++) {
chr = (char) getc(file);
map[row][column] = chr;
}
chr = (char) getc(file); //read \n but not write to array
}
row = 0;
for (row = 0; row < rows; row++) {
for (column = 0; column < columns; column++) {
printf("%c", map[row][column]);
}
printf("\n");
}
fclose(file);
free(map);
return 0;
}
至于你读取的文件类型和分配内存的方式,没有必要将整个文件读取两次:
.....*....................
...............*....*.....
..*.......*...............
............*.............
数据是面向行的,而且 --- 我想 --- 所有行都具有相同的列数。数据将存储在
char **map;
一个指针数组,所以每个map[i]
是char*
并且可以容纳一行数据。
fscanf()
是为使用表格数据而编写的,可能带有分隔符和不同的字段类型,例如 csv 文件包含许多 int
和 float
数据以 #
分隔。使用 fscanf()
读取字符没有多大意义,您可以使用 fgetc()
或 fread()
代替。
关于您的代码
- 如前所述,您需要
rewind
文件才能再次读取数据 - 另外,如前所述,您需要测试
'\n'
,因为它不是数据 - 始终根据说明符的数量测试
fscanf()
return
考虑到我上面所说的,下面的循环会消耗你的文件
rewind(file);
row = 0;
column = 0;
while (1 == fscanf(file, "%c", &map[row][column]))
{
if (map[row][column] == '\n')
{ row += 1, column = 0;
printf("\n");
}
else
{ printf("%c", map[row][column]);
column += 1;
}
}; // while();
fclose(file);
一般来说,在平坦的内存区域中使用地图数据更易于管理,仅使用 char*
,以 C 方式,逐行存储地图,而不是使用 char**
更灵活的方式
使用 char**
方式,您可以按行读取数据并将地图保留为字符串,因为它可能对显示和使用有用,同时仍然使用
map[][]
供参考。
我给你举个例子
此外,既然你说你有很多文件大小,你应该考虑将文件名作为参数传递
要获得更多控制,您可以使用一些封装并将数据放在 struct
中,例如
typedef struct
{
int cols;
int rows;
int size;
char** map;
} Grid;
这样你就可以拥有像
这样的功能int show_grid(Grid*,const char*);
并写
char title[100];
sprintf(title, "\n Map for %s\n",file_name);
show_grid(&gr, title);
在屏幕上看到
Map for file.txt
[18 rows, 26 columns]
....*.....................
..........................
........*........*........
.....*....................
...............*....*.....
..*.......*...............
............*.............
..........................
..............*...........
..................*.......
..*.......*......*........
....*..*..................
...**.....................
..........*...............
....................*.....
..........................
....**....................
......................*...
而且代码可以简单到4行:
void show_grid(Grid* g, const char* msg)
{
if (msg != NULL) printf("%s\n", msg);
printf("[%d rows, %d columns]\n",
g->rows, g->cols);
for (int i = 0; i < g->rows; i+=1)
printf("%s\n", g->map[i]);
printf("\n");
}
确定列大小
int ch = 0;
for (gr.cols = 0; ch != '\n'; gr.cols += 1)
{
ch = fgetc(F);
if (feof(F)) return -2;
}
您只需要找到第一行的结尾,因为所有行的大小都相同,并且内存是按行分配的。
在使用char*
和平坦区域的情况下,分配与文件一样大的区域可能更简单,使用stat
或ftell
来获取文件大小而不是读取文件两次。
按行块分配内存
使用这种方式速度更快,使用fgets()
读取数据每次调用消耗一整行。此外,由于数据无论如何都不是平坦的,您可以将线条保留为字符串以简化显示。见:
// blocksize in lines
int row = 0;
while (!feof(F))
{
gr.map[row] = (char*)malloc(gr.cols); // new row
fgets(gr.map[row], gr.cols, F);
gr.map[row][gr.cols - 2] = 0;
row += 1;
if (row == gr.size)
{ // expand block
int new_size = gr.size + BLKSIZE;
char** temp = (char**)realloc(gr.map, sizeof(char*)*new_size);
if (temp == NULL) break;
gr.map = temp;
gr.size = new_size;
};
};
fclose(F);
你可以有一个合适的BLKSIZE
不调整多次
在命令行传递文件名
这样你可以有一个默认值,但也可以在命令行中传递文件名,以便它可以在脚本中使用。由于显示功能方便地显示文件名,您可以轻松检查任意数量的文件,如
// open file as 'F'
const char* default_file_name = "file.txt";
char file_name[80];
FILE* F = NULL;
if (argc > 1)
strcpy(file_name, argv[1]);
else
strcpy(file_name, default_file_name);
F = fopen(file_name, "r");
if (F == NULL)
{
perror("Could not open file");
return -1;
}
例子
SO> .\f0-0922
Map for file.txt
[18 rows, 26 columns]
....*.....................
..........................
........*........*........
.....*....................
...............*....*.....
..*.......*...............
............*.............
..........................
..............*...........
..................*.......
..*.......*......*........
....*..*..................
...**.....................
..........*...............
....................*.....
..........................
....**....................
......................*...
SO> .\f0-0922 other.txt
Map for other.txt
[5 rows, 5 columns]
....*
...*.
..*..
.*...
*....
考试代码
#define BLKSIZE 20
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int cols;
int rows;
int size;
char** map;
} Grid;
void show_grid(Grid*,const char*);
void free_grid(Grid*);
int main(int argc, char** argv)
{
// open file as 'F'
const char* default_file_name = "file.txt";
char file_name[80];
FILE* F = NULL;
if (argc > 1)
strcpy(file_name, argv[1]);
else
strcpy(file_name, default_file_name);
F = fopen(file_name, "r");
if (F == NULL)
{
perror("Could not open file");
return -1;
}
// define grid
Grid gr = {0, 0, 0, NULL};
// set 'cols' to column size
int ch = 0;
for (gr.cols = 0; ch != '\n'; gr.cols += 1)
{
ch = fgetc(F);
if (feof(F)) return -2;
}
gr.cols = gr.cols + 1; // add space to a terminating 0
rewind(F); // roll back 1st line
gr.map = (char**)malloc((BLKSIZE * gr.cols) * sizeof(char*)); // initial size
gr.size = BLKSIZE;
// blocksize in lines
int row = 0;
while (!feof(F))
{
gr.map[row] = (char*)malloc(gr.cols); // new row
fgets(gr.map[row], gr.cols, F);
gr.map[row][gr.cols - 2] = 0;
row += 1;
if (row == gr.size)
{ // expand block
int new_size = gr.size + BLKSIZE;
char** temp = (char**)realloc(gr.map, sizeof(char*)*new_size);
if (temp == NULL) break;
gr.map = temp;
gr.size = new_size;
};
};
fclose(F);
gr.rows = row;
gr.cols -= 2;
char title[100];
sprintf(title, "\n Map for %s\n",file_name);
show_grid(&gr, title);
free_grid(&gr);
return 0;
}
void show_grid(Grid* g, const char* msg)
{
if (msg != NULL) printf("%s\n", msg);
printf("[%d rows, %d columns]\n", g->rows, g->cols);
for (int i = 0; i < g->rows; i += 1) printf("%s\n", g->map[i]);
printf("\n");
}
void free_grid(Grid* g)
{
for (int i = 0; i < g->rows; i += 1) free(g->map[i]);
free(g->map);
g->map = NULL;
return;
}