我有这个C程序尝试文本行读取功能,应该能够处理任意长度的行。它的工作原理是维护一个缓冲区,只要需要更多空间,缓冲区的大小就会加倍。
实际方法如下:
/*******************************************************************************
* Attempts to expand the line buffer. If succeeded, TRUE is returned. *
*******************************************************************************/
static char* try_expand(char* buffer, int* p_buffer_length)
{
*p_buffer_length *= 2;
puts("Before realloc");
char* s = realloc(buffer, *p_buffer_length);
puts("After realloc");
if (s)
{
return s;
}
// Once here, realloc failed.
char* s2 = malloc(*p_buffer_length);
if (!s2)
{
return NULL;
}
strncpy(s2, buffer, *p_buffer_length / 2);
free(buffer);
return s2;
}
我正在使用Mac OS X,每当缓冲区扩展时,程序崩溃并且系统报告:
对象0x100105568的malloc:***错误:释放对象的校验和不正确 - 对象可能在被释放后被修改。
其他一切都在这里:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HELP_FLAG "-h"
#define VERSION_FLAG "-v"
#define FLAG_DESC "%-5s"
#define INITIAL_BUFFER_SIZE 8
#define FALSE 0
#define TRUE (~FALSE)
/*******************************************************************************
* This routine removes all leading and trailing whitespace from a string, *
* doing that in-place. (Total of two passes.) *
*******************************************************************************/
static char* trim_inplace(char* start)
{
return start;
/*
for (char* end = &start[strlen(start) - 1];
isspace(*end) && end >= start; --end)
{
*end = '\0';
}
while (isspace(*start))
{
++start;
}
return start;*/
}
/*******************************************************************************
* Processes a single line and handles everything needed for dealing with lines *
* of arbitrary length. *
*******************************************************************************/
static int process_line(char** p_buffer, int* p_buffer_length, FILE* file)
{
size_t current_index = 0;
for (;;)
{
char* ret = fgets(*p_buffer + current_index, *p_buffer_length, file);
if (!ret)
{
//puts("!ret is true.");
return FALSE;
}
// Find out whether we have a newline character, which would imply that
// we have an entire line read.
for (size_t i = 0; i < *p_buffer_length; ++i)
{
if ((*p_buffer)[i] == '\n')
{
//(*p_buffer)[i + 1] = '\0';
puts(trim_inplace(*p_buffer));
return TRUE;
}
}
// -1 for skipping the NULL-terminator.
current_index += *p_buffer_length - 1;
char* new_buffer;
// Once here, the current line does not fit in 'p_buffer'. Expand the
// array by doubling its capacity.
if (!(new_buffer = try_expand(*p_buffer, p_buffer_length)))
{
perror("Could not expand the line buffer");
free(*p_buffer);
exit(EXIT_FAILURE);
}
else
{
*p_buffer = new_buffer;
}
}
}
/*******************************************************************************
* Processes a file. *
*******************************************************************************/
static void process_file(char** p_buffer, int* p_buffer_length, FILE* file)
{
while (!feof(file))
{
process_line(p_buffer, p_buffer_length, file);
}
}
/*******************************************************************************
* Prints the help message and exits. *
*******************************************************************************/
static void print_help()
{
printf("Usage: trim [" HELP_FLAG "] [" VERSION_FLAG "] " \
"[FILE1, [FILE2, [...]]]\n" \
" " FLAG_DESC " Print the help message and exit.\n" \
" " FLAG_DESC " Print the version message and exit.\n" \
" If no files specified, reads from standard input.\n",
HELP_FLAG,
VERSION_FLAG);
}
/*******************************************************************************
* Prints the version string. *
*******************************************************************************/
static void print_version()
{
printf("trim 1.618\n" \
"By Rodion \"rodde\" Efremov 08.04.2015 Helsinki\n");
}
/*******************************************************************************
* Prints the erroneous flag. *
*******************************************************************************/
static void print_bad_flag(const char* flag)
{
printf("Unknown flag \"%s\"\n", flag);
}
/*******************************************************************************
* Checks the flags. *
*******************************************************************************/
static void check_flags(int argc, char** argv)
{
for (size_t i = 1; i < argc; ++i)
{
if (strcmp(argv[i], HELP_FLAG) == 0)
{
print_help();
exit(EXIT_SUCCESS);
}
else if (strcmp(argv[i], VERSION_FLAG) == 0)
{
print_version();
exit(EXIT_SUCCESS);
}
else if (argv[i][0] == '-')
{
print_bad_flag(argv[i]);
exit(EXIT_FAILURE);
}
}
}
/*******************************************************************************
* The entry point for a trivial line trimmer. *
*******************************************************************************/
int main(int argc, char** argv)
{
check_flags(argc, argv);
int buffer_length = INITIAL_BUFFER_SIZE;
char* buffer = malloc(buffer_length);
if (argc < 2)
{
// If realloc changes the location of memory, we need to know this.
process_file(&buffer, &buffer_length, stdin);
fclose(stdin);
return EXIT_SUCCESS;
}
for (size_t i = 1; i < argc; ++i)
{
FILE* file = fopen(argv[i], "r");
if (!file)
{
perror("Error opening a file");
return (EXIT_FAILURE);
}
process_file(&buffer, &buffer_length, file);
fclose(file);
}
}
我所做的唯一观察是,如果输入行只需要行缓冲区的一次扩展,那么一切都是O.K.但是,如果输入行足够大,至少需要两次扩展,程序就会崩溃。我在这里做错了什么?
答案 0 :(得分:1)
当您在process_line()
中读取更多块时,传递的错误大小:
fgets(*p_buffer + current_index, *p_buffer_length, file);
应该是
fgets(*p_buffer + current_index, *p_buffer_length - current_index, file);