使用 scanf 从控制台读取无限行
reading an unbounded line from the console with scanf
我需要读取一个有限但长度没有限制的字符串。
我们只了解了 scanf
,所以我想我不能使用 fgets
。
无论如何,我 运行 这个代码在一个长度大于 5 的输入上。
char arr[5];
scanf("%s", arr);
char *s = arr;
while (*s != '[=10=]')
printf("%c", *s++);
scanf
一直在扫描和写入溢出的部分,但它看起来像 hack。这是一个好习惯吗?如果没有,应该怎么读?
注意:我们已经了解了 alloc
函数系列。
scanf
是这项工作的错误工具(对于大多数工作)。如果您需要使用此功能,请使用 scanf("%c", &c)
.
一次阅读一个 char
您的代码误用了 scanf()
:您正在传递 arr
,指向 char
的指针数组的地址,而不是 char
.[=30 的数组=]
你应该分配一个char
和malloc
的数组,读入字符,当它太小时用realloc
扩展它,直到你得到一个[=22] =] 或 EOF
.
如果你可以倒回 stdin
,你可以先用 scanf("%*s%n", &n);
计算要读取的字符数,然后将目标数组分配给 n+1
字节,rewind(stdin);
并使用 scanf("%s", buf);
将字符串重新读入缓冲区。
这是一项有风险的业务,因为某些流(例如控制台输入)无法倒带。
例如:
fpos_t pos;
int n = 0;
char *buf;
fgetpos(stdin, &pos);
scanf("%*[^\n]%n", &n);
fsetpos(stdin, &pos);
buf = calloc(n+1, 1);
scanf("%[^\n]", buf);
因为你应该只知道一些基本的 C
,我怀疑这个解决方案是否符合你的预期,但我想不出任何其他方法来使用标准 C 一步读取无界字符串.
如果您使用的是 glibc 并且可能会使用扩展,您可以这样做:
scanf("%a[^\n]", &buf);
PS: 故意忽略所有错误检查和处理,但应在您实际分配时处理。
尝试限制接受的字符数:
scanf("%4s", arr);
只是你写的超过了arr[5]
。 "Hopefully" 您一直在进程的分配内存上写入,但如果超出范围,您将得到 segmentation fault。
%as
或 %ms
(POSIX) 可用于此目的如果您将 gcc 与 glibc 一起使用。(不是 C 标准)
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *s;
scanf("%as", &s);
printf("%s\n", s);
free(s);
return 0;
}
缓冲区溢出是一种瘟疫,是最著名但最难以捉摸的错误之一。所以你绝对不应该依赖他们。
既然您了解了 malloc()
和朋友,我想您应该会利用它们。
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
// Array growing step size
#define CHUNK_SIZE 8
int main(void) {
size_t arrSize = CHUNK_SIZE;
char *arr = malloc(arrSize);
if(!arr) {
fprintf(stderr, "Initial allocation failed.\n");
goto failure;
}
// One past the end of the array
// (next insertion position)
size_t arrEnd = 0u;
for(char c = '[=10=]'; c != '\n';) {
if(scanf("%c", &c) != 1) {
fprintf(stderr, "Reading character %zu failed.\n", arrEnd);
goto failure;
}
// No more room, grow the array
// (-1) takes into account the
// nul terminator.
if(arrEnd == arrSize - 1) {
arrSize += CHUNK_SIZE;
char *newArr = realloc(arr, arrSize);
if(!newArr) {
fprintf(stderr, "Reallocation failed.\n");
goto failure;
}
arr = newArr;
// Debug output
arr[arrEnd] = '[=10=]';
printf("> %s\n", arr);
// Debug output
}
// Append the character and
// advance the end index
arr[arrEnd++] = c;
}
// Nul-terminate the array
arr[arrEnd++] = '[=10=]';
// Done !
printf("%s", arr);
free(arr);
return 0;
failure:
free(arr);
return 1;
}
考虑
1) malloc()
在许多系统上只分配内存,不使用它。直到分配内存后,才会出现下划线的物理内存使用情况。参见 Why is malloc not "using up" the memory on my computer?
2) 无限的用户输入是不现实的。鉴于应该使用一些上限来防止黑客和恶意用户,简单地使用大缓冲区。
如果您的系统可以使用这两个想法:
char *buf = malloc(1000000);
if (buf == NULL) return NULL; // Out_of_memory
if (scanf("%999999s", buf) != 1) { free(buf); return NULL; } //EOF
// Now right-size buffer
size_t size = strlen(buf) + 1;
char *tmp = realloc(buf, size);
if (tmp == NULL) { free(buf); return NULL; } // Out_of_memory
return tmp;
根据 @chqrlie 条评论进行了修正。
我需要读取一个有限但长度没有限制的字符串。
我们只了解了 scanf
,所以我想我不能使用 fgets
。
无论如何,我 运行 这个代码在一个长度大于 5 的输入上。
char arr[5];
scanf("%s", arr);
char *s = arr;
while (*s != '[=10=]')
printf("%c", *s++);
scanf
一直在扫描和写入溢出的部分,但它看起来像 hack。这是一个好习惯吗?如果没有,应该怎么读?
注意:我们已经了解了 alloc
函数系列。
scanf
是这项工作的错误工具(对于大多数工作)。如果您需要使用此功能,请使用 scanf("%c", &c)
.
char
您的代码误用了 scanf()
:您正在传递 arr
,指向 char
的指针数组的地址,而不是 char
.[=30 的数组=]
你应该分配一个char
和malloc
的数组,读入字符,当它太小时用realloc
扩展它,直到你得到一个[=22] =] 或 EOF
.
如果你可以倒回 stdin
,你可以先用 scanf("%*s%n", &n);
计算要读取的字符数,然后将目标数组分配给 n+1
字节,rewind(stdin);
并使用 scanf("%s", buf);
将字符串重新读入缓冲区。
这是一项有风险的业务,因为某些流(例如控制台输入)无法倒带。
例如:
fpos_t pos;
int n = 0;
char *buf;
fgetpos(stdin, &pos);
scanf("%*[^\n]%n", &n);
fsetpos(stdin, &pos);
buf = calloc(n+1, 1);
scanf("%[^\n]", buf);
因为你应该只知道一些基本的 C
,我怀疑这个解决方案是否符合你的预期,但我想不出任何其他方法来使用标准 C 一步读取无界字符串.
如果您使用的是 glibc 并且可能会使用扩展,您可以这样做:
scanf("%a[^\n]", &buf);
PS: 故意忽略所有错误检查和处理,但应在您实际分配时处理。
尝试限制接受的字符数:
scanf("%4s", arr);
只是你写的超过了arr[5]
。 "Hopefully" 您一直在进程的分配内存上写入,但如果超出范围,您将得到 segmentation fault。
%as
或 %ms
(POSIX) 可用于此目的如果您将 gcc 与 glibc 一起使用。(不是 C 标准)
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *s;
scanf("%as", &s);
printf("%s\n", s);
free(s);
return 0;
}
缓冲区溢出是一种瘟疫,是最著名但最难以捉摸的错误之一。所以你绝对不应该依赖他们。
既然您了解了 malloc()
和朋友,我想您应该会利用它们。
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
// Array growing step size
#define CHUNK_SIZE 8
int main(void) {
size_t arrSize = CHUNK_SIZE;
char *arr = malloc(arrSize);
if(!arr) {
fprintf(stderr, "Initial allocation failed.\n");
goto failure;
}
// One past the end of the array
// (next insertion position)
size_t arrEnd = 0u;
for(char c = '[=10=]'; c != '\n';) {
if(scanf("%c", &c) != 1) {
fprintf(stderr, "Reading character %zu failed.\n", arrEnd);
goto failure;
}
// No more room, grow the array
// (-1) takes into account the
// nul terminator.
if(arrEnd == arrSize - 1) {
arrSize += CHUNK_SIZE;
char *newArr = realloc(arr, arrSize);
if(!newArr) {
fprintf(stderr, "Reallocation failed.\n");
goto failure;
}
arr = newArr;
// Debug output
arr[arrEnd] = '[=10=]';
printf("> %s\n", arr);
// Debug output
}
// Append the character and
// advance the end index
arr[arrEnd++] = c;
}
// Nul-terminate the array
arr[arrEnd++] = '[=10=]';
// Done !
printf("%s", arr);
free(arr);
return 0;
failure:
free(arr);
return 1;
}
考虑
1) malloc()
在许多系统上只分配内存,不使用它。直到分配内存后,才会出现下划线的物理内存使用情况。参见 Why is malloc not "using up" the memory on my computer?
2) 无限的用户输入是不现实的。鉴于应该使用一些上限来防止黑客和恶意用户,简单地使用大缓冲区。
如果您的系统可以使用这两个想法:
char *buf = malloc(1000000);
if (buf == NULL) return NULL; // Out_of_memory
if (scanf("%999999s", buf) != 1) { free(buf); return NULL; } //EOF
// Now right-size buffer
size_t size = strlen(buf) + 1;
char *tmp = realloc(buf, size);
if (tmp == NULL) { free(buf); return NULL; } // Out_of_memory
return tmp;
根据 @chqrlie 条评论进行了修正。