我必须完成这项任务,而且我正在努力。
“编写一个名为sort_file的程序,它将文本文件作为输入,并生成一个输出文件,其中所有原始行都按字母顺序排列。
该计划应该做到以下几点:
提示用户输入和输出文件的名称。您可以使用适当的未排序数据准备输入文件。每行一个单词的简单文件就足够了。 声明并定义三个函数以执行以下任务: 将输入文件中的数据读入数组。 使用冒泡排序算法对数组进行排序。 将已排序的数据数组写入输出文件。“
我为这个程序创建了一个.txt文件进行排序,但是我被困住了,只是越来越困惑我越看它。这是我到目前为止所做的事情
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZEA 50
#define SIZEB 20
void readFile(char a[]);
void sortFile(char*s[50],int n);
void writeFile();
int main( void ) {
char name[20];
printf("PLease enter filename:");
scanf("%s", &*name);
readFile(&*name);
printf("\n");
sortFile(name);
printf("\n");
writeFile();
}
void readFile(char a[]) {
FILE *cfPtr = NULL;
char list[SIZEA][SIZEB];
int i = 0;
int j = 0;
cfPtr = fopen("unsorted.txt", "r");
while (fgets(list[i], SIZEA, cfPtr)) {
list[i][strlen(list[i]) - 1] = '\0';
i++;
}
j = i;
for (i = 0; i < j; i++) {
printf("%s\n", list[i]);
}
}
void sortfile(char*s[50], int n) {
int i;
int j;
int compare;
char temp[1][10];
for (i = 0; i < n; i++)
for (j = 0; j < n - 1; j++)
{
compare = strcmp(s[j], s[j+1]);
if (compare > 0)
{
strcpy(temp[0], s[j+1]);
strcpy(s[j+1], s[j]);
strcpy(s[j], temp[0]);
}
}
}
void writeFile() {
FILE *cfPtr = fopen("unsorted.txt", "w");
fwrite(list, sizeof(char), sizeof(list), cfPtr);
fclose(cfPtr);
}
但是我不确定要填充函数的参数是什么,或者热点以使程序的主体运行。我是新手,我发现这个话题很难理解。
答案 0 :(得分:1)
关于这种界限:
scanf("%s", &*name);
在C中,数组名称降级为数组第一个字节的地址。 (有几个例外,但它们不属于此处)所以该行应该是:
scanf("%s", name);
然而,这有几个问题。
格式说明符'%s'将允许用户溢出输入缓冲区。 %s格式说明符需要一个最大长度修饰符(并记住在使用%s格式说明符时将自动附加NUL字节。建议:
scanf("%*s", sizeof( name ) -1, name);
编译时(强烈建议执行编译和链接作为单独的步骤)始终启用所有警告,然后修复这些警告。 (对于gcc,至少使用:-Wall -Wextra -pedantic)
当原型化没有参数的函数时,不要使用:
void writeFile();
因为这告诉编译器会有参数,但它们尚未定义。而是使用:
void writeFile( void );
因为它告诉编译器没有参数。
注意:实际的函数声明可以/不应该在parens之间具有'void'
当声明变量和/或参数时使用有意义的名称。像char'a []'这样的参数名称(大多数)是没有意义的。而是使用类似的东西:
char * filename
or
char filename[]
当#defined'ing数值时,强烈建议用parens包围数值以避免某些“文本替换”错误。所以这个:
#define SIZEA 50
会更好地写成
#define SIZEA (50)
检查fopen()的返回值以确保操作成功:
FILE *fp = NULL:
...
if( NULL == ( fp = fopen( filename, "r" ) )
{ // then error occurred
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
将使用'fopen failed'文本以及相关的系统错误消息
写入stderr这一行:
list[i][strlen(list[i]) - 1] = '\0';
不一定会删除尾随换行符。 方案1:文件结束,文件中没有尾随换行符。场景2:在DOS / Windows上,换行符是2个字符,而不是1个字符。
正确的方法(还有其他几种方法)将是:
char *newline == NULL;
if( NULL != (newline = strstr( list[i], "\n" ) ) )
{
*newline = '\0';
}
关于这一行:
while (fgets(list[i], SIZEA, cfPtr))
1. if the input file contains more than SIZEA entries,
then the array list[] will be overrun,
resulting in undefined behaviour and can lead to a seg fault event.
2. the size of each entry in the array is SIZEB.
Therefore suggest using:
while ( i<SIZEA && fgets(list[i], SIZEB, cfPtr))
更健壮的方法是:char **list = NULL;
然后使用realloc()
更新列表中的条目数,并使用readline()
或getline()
输入/分配内存对于列表中的每个条目。
确保将每个结果指针传递给free()
以避免任何内存泄漏,
关于sortfile()函数:
这一行存在一些问题:
char temp[1][10];
1. an entry can be up to 20 characters line (SIZEB)
so 10 is inflexible and too short.
2. does not need to be a 2D array. Suggest:
char temp[SIZEB];
...
strcpy(temp, s[j+1]);
strcpy(s[j+1], s[j]);
strcpy(s[j], temp);
函数:writefile():
未能通过提示用户输入名称来实现输出文件名的问题场景。而是使用一些硬编码名称:unsorted.txt
。该文件名具有误导性,因为输出是排序的,而不是未排序的。
关于writefile()
中的这一行:
fwrite(list, sizeof(char), sizeof(list), cfPtr);
sizeof(char)
在标准中定义为1。
建议交换第二个和第三个参数。 但是,<newline>
中没有list[]
个字符,list[]
数组中的大多数行都有大量未初始化的字符。所以输出将是垃圾。建议:分别输出每一行:
char outBuf[SIZEB+3]; // +3 allows for newline and NUL byte
for( int i = 0; i < SIZEA; i++)
{
int bytecount = sprintf( outBuf, "%s\n", list[i] );
fwrite( outBuf, bytecount, 1, cfPtr );
}
注意:在SIZEA
循环中使用for()
假设list []数组使用所有条目,I.E。输入文件恰好包含50行。这可能不是真的,因此代码应该保留readfile()
函数中读取的行数,并在sortfile()
和writefile()
函数中使用该数字。
关于SIZEA
和SIZEB
。
#define MAX_ROWS (50)
#define MAX_COLUMNS (20)