提问人:CVB 提问时间:7/13/2023 最后编辑:chqrlieCVB 更新时间:7/14/2023 访问量:150
如果没有换行符,则代码不返回行
Code not returning line if there is no newline character
问:
几个月前,我开始用 C 语言编写代码。我编写了这段代码来返回文件中的每一行。它必须使用任何缓冲区大小,现在它确实如此,并且它应该返回文件中存在的每一行。但是,如果我的行末尾没有换行符,我的代码就不会返回任何内容。
到目前为止,我认为问题出在我的职能上。我可以看到,我可以将没有换行符的行放入行变量以及存储变量中。但是,在代码清理存储后,我相信它进入了条件,因此在返回我的行变量中的行之前结束了我的程序。我添加了这个条件作为终止循环的一种方式,所以如果我删除它,代码就会卡在无限循环上。我尝试了其他方法,我设法获得了所有行,但它仅适用于大于文件大小的缓冲区大小,我通过将变量更改为等于 来做到这一点。在这种情况下,对于较小的缓冲区,我会得到这些线,但它们会被“破坏”。get_next_line
if
(stash[0] == '\0')
main
while
i
find_nl(line) + (line[0] != '\0');
我正处于学习 C 语言的起步阶段,因此我将不胜感激任何改进此代码的输入。先谢谢你。
这是程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUFFER_SIZE 100
int find_nl(char *stash);
char *get_line(char *stash, char *line);
void ft_clean(char *stash);
char *get_next_line(int fd)
{
static char stash[BUFFER_SIZE + 1];
char *line;
int n;
int i;
if (fd < 0 || BUFFER_SIZE <= 0)
return (NULL);
i = 0;
n = 0;
line = NULL;
while (!i)
{
if (!stash[0])
n = read(fd, stash, BUFFER_SIZE);
if (n == -1)
return (NULL);
line = get_line(stash, line);
i = find_nl(stash) + (line == NULL);
if (stash[0] == '\0')
return (NULL);
ft_clean(stash);
}
return (line);
}
int find_nl(char *stash)
{
size_t i;
if (stash == NULL)
return (0);
i = 0;
while (stash[i])
{
if (stash[i] == '\n')
return (1);
i++;
}
return (0);
}
char *get_line(char *stash, char *line)
{
size_t len;
size_t i;
size_t j;
char *nline;
len = 0;
j = 0;
while (stash[len] && stash[len] != '\n')
len++;
if (line == NULL)
i = 0;
else
while (line[i])
i++;
nline = (char *)malloc((len + i + 1) * sizeof(char));
if (nline == NULL)
return (NULL);
while (line && line[j])
{
nline[j] = line[j];
j++;
}
i = 0;
while (i < len)
{
nline[j] = stash[i];
i++;
j++;
}
nline[j] = '\0';
return (nline);
}
void ft_clean(char *stash)
{
size_t stash_len;
size_t len;
size_t i;
len = 0;
stash_len = 0;
i = 0;
if (stash == NULL)
return ;
while (stash[len])
{
if (stash[len] == '\n')
{
len++;
break ;
}
len++;
}
while (stash[stash_len] != '\0')
stash_len++;
while (i < stash_len - len + 1)
{
stash[i] = stash[i + len];
i++;
}
stash[i] = '\0';
}
int main(void) {
char *line;
while ((line = get_next_line(0)) != NULL) {
printf("[%s]\n", line);
free(line);
}
return 0;
}
如果按如下方式调用,则其工作原理:
printf 'abc\n' | ./a
但以下内容不会输出任何内容:
printf 'abc' | ./a
答:
不是直接的答案,但我认为有更好的方法。您当前的代码很难理解,不是很模块化和错误。
您正在使用和在您的代码中,通常这没有错,但是由于您有malloc()
free()
该项目要求我只使用低级系统调用
你不能使用它们。
我建议在调用此函数时使用 a 并将指针传递给该变量,而不是在函数内部使用变量(在您的情况下为 for,并且可能更多,因为这是更好的实现所必需的)。这也将允许您同时(准)对多个文件使用此函数。static
get_next_line()
stash
struct
struct
像这样的东西:
struct File_T
{
int fd;
char buffer[BUFFER_SIZE];
size_t currentLineEnd; //position of the first char after the current line
size_t validChars;
};
void file_init(struct File_T *file, int fd);
const char *file_getNextLine(struct File_T *file);
void file_init(struct File_T *file, int fd)
{
file->fd=fd;
file->currentLineEnd=0;
file->validChars=0;
}
当您从文件中获取时,您可能会收到字节,但其本身不会在末尾添加 a,即 不填充字符串,而是填充可以包含任何数据的缓冲区。您需要使用 的返回值来检查读取的字符数。我建议,你读到最后一个有效字符之后(从上次你或0设置),你可以在读字符的数量。另请注意,返回的是类型而不是 .read()
'\0'
read()
'\0'
read()
read()
buffer
read()
validChars
read()
ssize_t
int
ssize_t n = read(file->fd,&file->buffer[file->validChars],BUFFER_SIZE-file->validChars-1); //-1 so we can always add a '\0'
if( n<0 )
{ /*do some error handling here*/ }
file->validChars+=n;
为了避免,您可以在 (直到你到达 ,之后你要么必须阅读更多内容,要么必须决定缓冲区中没有剩余空间,或者你阅读最后一行,那么你必须在位置之后使用下一个字符)并将其替换为 ,设置为 1 个字符,然后返回指向开头的指针。下次调用时,将所有有效内容从 till 移动到开头(使用或您自己的实现)并重复该过程。malloc()
'\n'
buffer
validChars
validChars
'\0'
currentLineEnd
file_getNextLine()
currentLineEnd
validChars
memmove()
编辑:
如果需要处理任意长度的行,而不设置那么大,则可以为字符串分配新的内存来存储,而不使用 .由于您不想使用 ,因此可以使用 保留缓冲区。喜欢这个:BUFFER_SIZE
currentLineEnd
malloc()
mmap()
char *buffer=mmap(NULL,newBufferSize,PROT_WRITE|PROT_READ,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
以后别忘了。munmap()
评论
mmap()
malloc()
calloc()
free()
mmap()
也许这个功能会有所帮助。它被称为 fgets,你可以用它来代替读取函数。如果你不能使用,只需复制它背后的想法。
定义:char *fgets(char *restrict s, int n, FILE *restrict stream);
fgets() 函数应将字节从流中读取到 s 指向的数组中,直到读取 n-1 个字节,或者读取<换行符>并将其传输到 s,或者遇到文件结束情况。然后,该字符串以 null 字节终止。成功完成后,fgets() 将返回 s。如果流位于文件末尾,则应设置流的文件结束指示符,并且 fgets() 应返回空指针。如果发生读取错误,则应设置流的错误指示器,fgets() 应返回空指针。
将其与此结合使用:
int get_cleaned_line(char str[], int maxlen) //I never used read, idk fd what it is
{ //you can use BUFFER_SIZE instead of maxlen
int len = -1;
if (fgets(str, maxlen, stdin) != NULL) //I think stdin it's like fd=0(input from keyboard)
{ //whatever use the proper input file
len = 0;
while(str[len] != '\0')
len++;
if (len > 0 && str[len-1] == '\n')
{
str[len-1] = '\0';
len--;
}
return len; //can be useful
}
我一年前刚学过C语言,我真的很讨厌弦乐,希望这有帮助。
评论
fread
fgets
maxlen
事实证明,这相当复杂。这是我解决问题的尝试:
read_line.h
:
#include <stdbool.h>
#define GET_LINE_BUFFER_SIZE 100
typedef struct {
int fd;
int error;
int eof;
size_t in_buf;
char buffer[ GET_LINE_BUFFER_SIZE ];
} ReadLineData;
ReadLineData *ReadLine_new( int fd );
void ReadLine_init( ReadLineData *data, int fd );
void ReadLine_destroy( ReadLineData *data );
void ReadLine_free( ReadLineData *data );
// Returns -1 on error. *line_ptr is set to NULL. errno is set.
// Returns 0 on EOF. *line_ptr is set to NULL.
// Returns +1 on success. *line_ptr is set to a string to free.
int ReadLine_read_line( ReadLineData *data, char **line_ptr );
read_line.c
:
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "read_line.h"
void ReadLine_init( ReadLineData *data, int fd ) {
data->fd = fd;
data->error = 0;
data->eof = 0;
data->in_buf = 0;
}
// Returns NULL on error. errno is set.
ReadLineData *ReadLine_new( int fd ) {
ReadLineData *data = malloc( sizeof( ReadLineData ) );
if ( data )
ReadLine_init( data, fd );
return data;
}
void ReadLine_destroy( ReadLineData *data ) {
(void)data; // Nothing to do.
}
void ReadLine_free( ReadLineData *data ) {
ReadLine_destroy( data );
free( data );
}
static char *find_lf( ReadLineData *data ) {
char *p = data->buffer;
for ( size_t n = data->in_buf; n--; ++p ) {
if ( *p == '\n' ) {
return p;
}
}
return NULL;
}
// Returns false on error. errno is set.
// Returns true on success.
static bool safe_size_add( size_t *acc_ptr, size_t to_add ) {
if ( to_add > SIZE_MAX - *acc_ptr ) {
errno = ENOMEM;
return false;
}
return true;
}
// Returns false on error. errno is set.
// Returns true on success.
static bool move_to_line(
ReadLineData *data,
char **line_ptr, // in-out. Set to NULL on error.
size_t *line_len_ptr, // in-out.
size_t n
) {
char *line = *line_ptr;
size_t line_len = *line_len_ptr;
// Calculate new line size.
// Protect against overflow.
size_t new_line_len_p1 = line_len;
if ( !safe_size_add( &new_line_len_p1, n ) )
goto ERROR;
if ( !safe_size_add( &new_line_len_p1, 1 ) )
goto ERROR;
// Enlarge the buffer.
char *new_line = realloc( line, new_line_len_p1 );
if ( !new_line )
goto ERROR;
line = new_line;
// Copy from the buffer.
memmove( line + line_len , data->buffer, n );
line_len += n;
line[ line_len ] = 0;
// Remove from the buffer.
data->in_buf -= n;
memcpy( data->buffer, data->buffer + n, data->in_buf );
*line_ptr = line;
*line_len_ptr = line_len;
return true;
ERROR:
free( line );
*line_ptr = NULL;
return false;
}
// Returns -1 on error. *line_ptr is set to NULL. errno is set.
// Returns 0 on EOF. *line_ptr is set to NULL.
// Returns +1 on success. *line_ptr is set to a string to free.
int ReadLine_get_line( ReadLineData *data, char **line_ptr ) {
*line_ptr = NULL;
size_t line_len = 0;
if ( data->eof )
return 0;
while ( 1 ) {
if ( data->in_buf ) {
char *lf = find_lf( data );
if ( lf )
return move_to_line( data, line_ptr, &line_len, lf - data->buffer + 1 ) ? +1 : -1;
// We didn't find a LF, so the whole buffer is part of the line.
if ( !move_to_line( data, line_ptr, &line_len, data->in_buf ) )
return -1;
}
// We need to read more.
ssize_t bytes_read = read( data->fd, data->buffer, GET_LINE_BUFFER_SIZE );
if ( bytes_read < 0 ) {
data->eof = 1;
data->error = 1;
free( *line_ptr );
*line_ptr = NULL;
return -1;
}
if ( bytes_read == 0 ) {
data->eof = 1;
return line_len ? +1 : 0;
}
data->in_buf = bytes_read;
}
}
程序:
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include "read_line.h"
int main( void ) {
ReadLineData data;
ReadLine_init( &data, 0 );
while ( 1 ) {
char *line;
int rv = ReadLine_read_line( &data, &line );
if ( rv < 0 )
err( EXIT_FAILURE, "read_line" );
if ( rv == 0 )
break;
printf( "[%s]\n", line );
free( line );
}
ReadLine_destroy( &data );
}
它使用 、 和 。如果你想避免这些,前两个很容易自己重新实现。 重新实现会更棘手,并且需要类似 .这是留给读者的练习。memcpy
memmove
realloc
realloc
mmap
评论
read()
末尾后不添加字节。您必须确保它在那里或使用其他方法来检查它。'\0'
main