我遇到这样的问题: 编写一个程序来读取多行文本文件,并将“N”个最长行写入stdout。在命令行中指定要读取的文件的位置。
现在我写了这样的程序:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
int a,k,n,i=0,j;
int part;
char ar[1000][1000],str[1000];
/* char file[200]; */
/* scanf("%s",file); */
FILE *f = fopen(argv[1],"r");
if ( f == NULL || argc < 2)
{
return 0;
}
fscanf(f,"%d",&a);
while (fscanf(f,"%s",str)==1)
{
strcpy(ar[i++],str);
for ( k = 0 ; k < i ; k++ )
{
for ( j = k ; j < i ; j++)
{
if ( strlen(ar[k]) < strlen(ar[j]))
{
strcpy(str,ar[k]);
strcpy(ar[k],ar[j]);
strcpy(ar[j],str);
}
}
}
}
for ( j = 0 ; j < a ; j++ )
{
puts(ar[j]);
}
return 0;
}
首先,它对我来说效果很好但是在提交时它给了我运行时错误。 其次我想用指针和动态分配内存来做。我怎么能这样做?
抱歉,我上床睡了一段时间。你能解释一下我的代码有什么问题吗?为什么它不起作用。我想没有人解释我在哪里做错了。请告诉我如何在发布问题几个小时后引起人们的注意。再次感谢所有人给我这么多时间。
答案 0 :(得分:1)
如果你真的想节省内存,那么你不应该复制文件的任何内容,而是将mmap
复制到你的进程中,只将偏移量保存到代码中最长的行中。在较长的文件上,由于将内存管理外包给内核,因此可以提供更高的性能。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc, char ** argv) {
char * filename = argv[1];
if (!filename) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(-1);
}
int fd = open(filename, O_RDONLY);
if (fd < 0) {
perror("open");
abort();
}
struct stat file_stats;
if(fstat(fd, &file_stats)) {
perror("fstat");
abort();
}
char * file_contents = mmap(NULL // anywhere
, file_stats.st_size + 1 // file length + 1 byte for null terminator
, PROT_READ // we only need read only
, MAP_PRIVATE // this doesn't really matter since we are read only
, fd // from this file descriptor
, 0); // from beginning
if (file_contents == MAP_FAILED) {
perror("mmap");
abort();
}
// optional
// Expect page references in sequential order.
// Hence, pages can be aggressively read ahead, and may be freed soon after they are accessed.
madvise(file_contents, file_stats.st_size + 1, MADV_SEQUENTIAL);
struct {
size_t start;
size_t end;
} longest_lines[10]; // struct to hold ofsets for longest lines
memset(longest_lines, 0, sizeof(longest_lines)); // initialise
int shortest_line_id = 0;
char * start = file_contents;
char * next;
// while we are not at the end
while (start < file_contents + file_stats.st_size) {
if (!(next = strchr(start, '\n'))) // if line ternimator wasn't found, then go to the end
next = file_contents + file_stats.st_size;
size_t line_length = next - start;
// if this line is longer then our shortest
if (line_length > longest_lines[shortest_line_id].end - longest_lines[shortest_line_id].start) {
longest_lines[shortest_line_id].start = start - file_contents;
longest_lines[shortest_line_id].end = next - file_contents;
// determine new shortest line
int i;
for (i = 0; i < sizeof(longest_lines)/sizeof(*longest_lines); i++) {
if (
longest_lines[i].end - longest_lines[i].start
<
longest_lines[shortest_line_id].end - longest_lines[shortest_line_id].start
)
shortest_line_id = i;
}
}
// next line starts at this offset
start = next + 1;
}
int i; // print them
for (i = 0; i < sizeof(longest_lines)/sizeof(*longest_lines); i++) {
printf("%.*s\n", (int)(longest_lines[i].end - longest_lines[i].start), file_contents + longest_lines[i].start);
}
return 0;
}
答案 1 :(得分:1)
因为它似乎是某种功课或某事。我不会提供完整的解决方案。但我会尝试给你一些你可以开始的和平。
您应该按照职责划分您的任务,并尝试创建您的程序模块化。
以下几项任务似乎是:
char*
- 数组(char**
)
malloc()
与反变量getline()
获取指向已分配内存部分的指针getline
需要您做很多工作,但您也可以将以下功能合并到同一个:
fgets
malloc
realloc
strlen
char**
进行排序
strlen
和一些swap
- 函数来实现此目标(通过简单的应用......如bubblesort)N
char**
的第一行printf
行
free()
!有关提示,请参阅以下post。
请注意,我提供的步骤不会针对内存或cpu使用进行优化。但是如果你的程序有效并且你已经完成了你已经完成的工作,这可能是下一步。
此任务还显示了现代编程语言的巨大好处,其中这样的任务将是一个五行脚本。这是F#版本,可以执行您想要达到的目标:
open System
open System.IO
open System.Linq
[<EntryPoint>]
let main args =
try
let linesToPrint = Int32.Parse(args.ElementAt(0))
let path = @"C:\Users\XYZ\Desktop\test.txt"
File.ReadAllLines(path).OrderByDescending(fun line -> line.Length).Take(linesToPrint).ToArray()
|> Array.iter(printfn "%s")
with
| ex -> eprintfn "%s" ex.Message
0
请注意解决方案的排序和可读性。 然而,有必要在c或者也可以在汇编程序中编写这样的任务,以便更深入地了解计算机的工作方式。
答案 2 :(得分:1)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int a, n=0, i;
char **ar, str[1000];
int *lens;
FILE *f = fopen(argv[1],"r");
if ( f == NULL || argc < 2){
return 0;
}
fscanf(f,"%d",&a);
if(NULL==(ar=calloc(a, sizeof(char*))) || NULL==(lens=calloc(a, sizeof(int)))){
perror("malloc");
return -1;
}
while (fscanf(f, " %999[^\n]", str)==1){
++n;
int len = strlen(str);
for(i = 0;i < a;++i){
if(lens[i] < len){
free(ar[a-1]);
memmove(&lens[i+1], &lens[i], (a-i-1)*sizeof(int));
memmove(&ar[i+1], &ar[i], (a-i-1)*sizeof(char*));
ar[i]=strdup(str);
lens[i]=len;
break;
}
}
}
fclose(f);
for (i = 0 ; i < n && i < a ; i++ ){
puts(ar[i]);
free(ar[i]);
}
free(ar);free(lens);
return 0;
}
答案 3 :(得分:0)
如果你很高兴坚持你的最大行长1000,你可以这样做:
char (*ar)[1000] = malloc(a * sizeof *ar);
if (!ar)
// error handling....
然后使用之前使用过的ar
。
有一个问题:fscanf(f,"%s",str)
。这读取一个单词,不进行长度检查。我想你实际上想读一整行,而不是溢出你的缓冲区:
fgets(str, sizeof str, f);
如果您愿意,可以在此之后从str
删除换行符,但实际上它对您的程序没有任何影响。
您的算法目前存在问题;你把每一行读成ar
。相反,您应该将ar
的大小设为a
(我认为a
应为N
),取出行{{1}只有当它大于当前最小成员时才插入该行。