Answers:
您需要动态内存管理,并使用该fgets
功能读取您的行。但是,似乎无法查看它读取了多少个字符。因此,您使用fgetc:
char * getline(void) {
char * line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if(line == NULL)
return NULL;
for(;;) {
c = fgetc(stdin);
if(c == EOF)
break;
if(--len == 0) {
len = lenmax;
char * linen = realloc(linep, lenmax *= 2);
if(linen == NULL) {
free(linep);
return NULL;
}
line = linen + (line - linep);
linep = linen;
}
if((*line++ = c) == '\n')
break;
}
*line = '\0';
return linep;
}
注意:切勿使用gets!它不执行边界检查,并且可能溢出缓冲区
fgetc_unlocked
如果不是线程安全而是性能的考虑,则可以使用便携式性较差的方法。
读取静态分配行的一种非常简单但不安全的实现:
char line[1024];
scanf("%[^\n]", line);
一个比较安全的实现是:没有可能发生缓冲区溢出,但是有可能无法读取整行:
char line[1024];
scanf("%1023[^\n]", line);
在声明变量的指定长度和格式字符串中指定的长度之间不是“一”的差异。这是一个历史文物。
gets
完全从标准中删除,它也遭受了完全相同的问题
因此,如果您要查找命令参数,请看一下Tim的答案。如果您只想从控制台读取一行:
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}
是的,它不安全,您可以执行缓冲区溢出操作,不检查文件结尾,不支持编码和许多其他功能。实际上,我什至都没有想过是否做过这些事情。我同意我有点搞砸了。像上面一样。实际上,我认为,如果您实际上尝试编写这100行代码,那么您犯的错误将比选择gets时要犯的错误还要多;)
gets
不再存在,因此在C11中不起作用。
getline
可运行的例子
提到了这个答案,但这是一个例子。
它是POSIX 7,为我们分配了内存,并很好地在循环中重用了分配的缓冲区。
指针newbs,请阅读以下内容:为什么getline的第一个参数是指针“ char **”而不是“ char *”的指针?
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *line = NULL;
size_t len = 0;
ssize_t read = 0;
while (read != -1) {
puts("enter a line");
read = getline(&line, &len, stdin);
printf("line = %s", line);
printf("line length = %zu\n", read);
puts("");
}
free(line);
return 0;
}
glibc实施
没有POSIX?也许您想看看glibc 2.23的实现。
解析为getdelim
,这是getline
带有任意行终止符的简单POSIX超集。
每当需要增加内存时,它将分配的内存加倍,并且看起来是线程安全的。
它需要一些宏扩展,但是您不可能做得更好。
len
,当阅读时也提供了长度
man getline
。len
是现有缓冲区的长度,0
是魔术,并告诉它进行分配。读取是读取的字符数。缓冲区大小可能大于read
。
像我一样,许多人来此帖子的标题与搜索内容相匹配,尽管描述中提到的是可变长度。对于大多数情况,我们事先知道长度。
如果您确实知道长度,请尝试以下操作:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
来源:https : //www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
如建议的那样,您可以使用getchar()从控制台读取,直到返回行尾或EOF,从而建立自己的缓冲区。如果您无法设置合理的最大行大小,则会动态增加缓冲区。
您还可以将fgets用作获取以C终止的字符串作为行的安全方法:
#include <stdio.h>
char line[1024]; /* Generously large value for most situations */
char *eof;
line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0'; /* Ensure no false-null at end of buffer */
eof = fgets(line, sizeof(line), stdin);
如果您用尽了控制台输入或由于某种原因操作失败,则返回eof == NULL,并且行缓冲区可能不变(这就是将第一个字符设置为'\ 0'的原因)。
fgets不会使line []溢出,并且将确保在成功返回的最后一个接受的字符之后为null。
如果到达了行尾,则终止符'\ 0'之前的字符将是'\ n'。
如果在结尾“ \ 0”之前没有终止符“ \ n”,则可能是有更多数据,或者下一个请求将报告文件结束。您将必须执行另一个fget以确定哪个是哪个。(就此而言,使用getchar()循环更容易。)
在上面的(更新的)示例代码中,如果成功获取fget之后,line [sizeof(line)-1] =='\ 0',则说明缓冲区已完全填满。如果该职位以'\ n'开头,则说明您很幸运。否则,stdin中可能有更多数据或文件结尾。(当缓冲区未完全填满时,您可能仍位于文件末尾,并且当前行的末尾也可能没有'\ n'。由于您必须扫描字符串才能找到和/或删除字符串末尾(缓冲区中的第一个'\ 0')之前的任何'\ n',我倾向于首先使用getchar()。)
做您需要做的事情以处理仍然多于您作为第一块读取的行数的行。可以使动态增长缓冲区的示例与getchar或fgets一起使用。需要注意一些棘手的边缘情况(例如,记住要在缓冲区扩展之前,将下一个输入开始存储在结束先前输入的'\ 0'位置)。
如何从C中的控制台读取一行?
建立自己的功能是帮助您从控制台读取一行的方法之一
我正在使用动态内存分配来分配所需的所需内存量
当我们将要耗尽分配的内存时,我们尝试将内存大小增加一倍
在这里,我使用循环使用该getchar()
功能逐个扫描字符串的每个字符,直到用户输入'\n'
或EOF
字符为止。
最后,我们在返回行之前删除所有额外分配的内存
//the function to read lines of variable length
char* scan_line(char *line)
{
int ch; // as getchar() returns `int`
long capacity = 0; // capacity of the buffer
long length = 0; // maintains the length of the string
char *temp = NULL; // use additional pointer to perform allocations in order to avoid memory leaks
while ( ((ch = getchar()) != '\n') && (ch != EOF) )
{
if((length + 1) >= capacity)
{
// resetting capacity
if (capacity == 0)
capacity = 2; // some initial fixed length
else
capacity *= 2; // double the size
// try reallocating the memory
if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
}
line[length] = (char) ch; //type casting `int` to `char`
}
line[length + 1] = '\0'; //inserting null character at the end
// remove additionally allocated memory
if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
return line;
}
现在您可以通过以下方式阅读整行内容:
char *line = NULL;
line = scan_line(line);
这是使用函数的示例程序scan_line()
:
#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions
char* scan_line(char *line)
{
..........
}
int main(void)
{
char *a = NULL;
a = scan_line(a); //function call to scan the line
printf("%s\n",a); //printing the scanned line
free(a); //don't forget to free the malloc'd pointer
}
样本输入:
Twinkle Twinkle little star.... in the sky!
样本输出:
Twinkle Twinkle little star.... in the sky!
前段时间我遇到了同样的问题,这是我的解决方案,希望对您有所帮助。
/*
* Initial size of the read buffer
*/
#define DEFAULT_BUFFER 1024
/*
* Standard boolean type definition
*/
typedef enum{ false = 0, true = 1 }bool;
/*
* Flags errors in pointer returning functions
*/
bool has_err = false;
/*
* Reads the next line of text from file and returns it.
* The line must be free()d afterwards.
*
* This function will segfault on binary data.
*/
char *readLine(FILE *file){
char *buffer = NULL;
char *tmp_buf = NULL;
bool line_read = false;
int iteration = 0;
int offset = 0;
if(file == NULL){
fprintf(stderr, "readLine: NULL file pointer passed!\n");
has_err = true;
return NULL;
}
while(!line_read){
if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
free(tmp_buf);
break;
}
if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
line_read = true;
offset = DEFAULT_BUFFER * (iteration + 1);
if((buffer = realloc(buffer, offset)) == NULL){
fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
free(tmp_buf);
has_err = true;
return NULL;
}
offset = DEFAULT_BUFFER * iteration - iteration;
if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
fprintf(stderr, "readLine: Cannot copy to buffer\n");
free(tmp_buf);
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
free(tmp_buf);
iteration++;
}
return buffer;
}
goto
用来处理错误情况,您的代码将变得更加简单。但是,您是否不认为可以重用tmp_buf
,而不是malloc
在循环中一遍又一遍地使用相同的大小?
has_err
报告错误使此函数在线程上不安全,使用起来也不舒服。不要那样做。您已经通过返回NULL来指示错误。还有空间认为打印的错误消息在通用库函数中不是一个好主意。
像这样:
unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
char * strbfr;
int c;
unsigned int i;
i = 0;
strbfr = (char*)malloc(sizeof(char));
if(strbfr==NULL) goto error;
while( (c = getchar()) != '\n' && c != EOF )
{
strbfr[i] = (char)c;
i++;
strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1));
//on realloc error, NULL is returned but original buffer is unchanged
//NOTE: the buffer WILL NOT be NULL terminated since last
//chracter came from console
if(strbfr==NULL) goto error;
}
strbfr[i] = '\0';
*pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
return i + 1;
error:
*pStrBfr = strbfr;
return i + 1;
}
从控制台读取行的最好,最简单的方法是使用getchar()函数,从而可以一次将一个字符存储在一个数组中。
{
char message[N]; /* character array for the message, you can always change the character length */
int i = 0; /* loop counter */
printf( "Enter a message: " );
message[i] = getchar(); /* get the first character */
while( message[i] != '\n' ){
message[++i] = getchar(); /* gets the next character */
}
printf( "Entered message is:" );
for( i = 0; i < N; i++ )
printf( "%c", message[i] );
return ( 0 );
}
此函数应执行您想要的操作:
char* readLine( FILE* file )
{
char buffer[1024];
char* result = 0;
int length = 0;
while( !feof(file) )
{
fgets( buffer, sizeof(buffer), file );
int len = strlen(buffer);
buffer[len] = 0;
length += len;
char* tmp = (char*)malloc(length+1);
tmp[0] = 0;
if( result )
{
strcpy( tmp, result );
free( result );
result = tmp;
}
strcat( result, buffer );
if( strstr( buffer, "\n" ) break;
}
return result;
}
char* line = readLine( stdin );
/* Use it */
free( line );
我希望这有帮助。
fgets( buffer, sizeof(buffer), file );
不是sizeof(buffer)-1
。fgets
为终止null留出空间。
while (!feof(file))
总是错误的,这只是错误使用的又一个示例。