通过 C 中的标准输入读取大型列表
reading large lists through stdin in C
如果我的程序要通过 stdin
传入大量数字列表,那么最有效的读取方法是什么?
我要传递给程序的输入将采用以下格式:
3,5;6,7;8,9;11,4;;
我需要处理输入,以便我可以使用冒号之间的数字(即我希望能够使用 3 和 5、6 和 7 等)。 ;;
表示它是行尾。
我正在考虑使用缓冲 reader 读取整行然后使用 parseInt。
这是最有效的方法吗?
我对 C 有点生疏,但这对你有用吗?
char[1000] remainder;
int first, second;
fp = fopen("C:\file.txt", "r"); // Error check this, probably.
while (fgets(&remainder, 1000, fp) != null) { // Get a line.
while (sscanf(remainder, "%d,%d;%s", first, second, remainder) != null) {
// place first and second into a struct or something
}
}
您可以使用 fgets or fgetc. You could also use getline() 从 stdin 读取,因为您正在从 stdin 读取。
读入该行后,您可以使用 strtok() with the delimiter for ";" to split the string into pieces at the semicolons. You can loop through until strok() is null, or in this case, ';'. Also in C you should use atoi() 将字符串转换为整数。
例如:
int length = 256;
char* str = (char*)malloc(length);
int err = getline(&str, &length, stdin);
我会读入命令参数,然后使用 strtok()
库方法
进行解析
http://man7.org/linux/man-pages/man3/strtok.3.html
(上面URL引用的网页还有使用方法的代码示例。)
这是一个可行的解决方案
一种方法是使用 strtok() 并将值存储在数组中。理想情况下,动态分配。
int main(int argc, char *argv[])
{
int lst_size=100;
int line_size=255;
int lst[lst_size];
int count=0;
char buff[line_size];
char * token=NULL;
fgets (buff, line_size, stdin); //Get input
通过传递“,”和“;”来使用 strtok作为分隔符。
token=strtok(buff, ";,");
lst[count++]=atoi(token);
while(token=strtok(NULL, ";,")){
lst[count++]=atoi(token);
}
最后你必须考虑双“;;”通过将计数减 1,因为对于这种情况 atoi(token) 将 return 0 并将其存储在第 n 个索引中。你不想要的。
count--;
}
getchar_unlocked()
就是您要找的。
代码如下:
#include <stdio.h>
inline int fastRead_int(int * x)
{
register int c = getchar_unlocked();
*x = 0;
// clean stuff in front of + look for EOF
for(; ((c<48 || c>57) && c != EOF); c = getchar_unlocked());
if(c == EOF)
return 0;
// build int
for(; c>47 && c<58 ; c = getchar_unlocked()) {
*x = (*x<<1) + (*x<<3) + c - 48;
}
return 1;
}
int main()
{
int x;
while(fastRead_int(&x))
printf("%d ",x);
return 0;
}
对于输入 1;2;2;;3;;4;;;;;54;;;;
上面的代码生成 1 2 2 3 4 54
.
我保证,这个解决方案比本主题中介绍的其他解决方案快得多。它不仅使用getchar_unlocked()
,还使用register
、inline
以及乘以10的技巧:(*x<<1) + (*x<<3)
.
祝你好运,找到更好的解决方案。
另一种相当优雅的处理方法是允许 strtol
通过将要读取的字符串推进到 strtol
返回的 endptr
来解析输入。根据需要结合数组 allocated/reallocated ,您应该能够处理任何长度的行(直到内存耗尽)。下面的示例使用单个数组作为数据。如果你想存储多行,每行作为一个单独的数组,你可以使用相同的方法,但从指向 int 指针数组的指针开始。 (即 int **numbers
并分配指针,然后分配每个数组)。如果您有任何问题,请告诉我:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define NMAX 256
int main () {
char *ln = NULL; /* NULL forces getline to allocate */
size_t n = 0; /* max chars to read (0 - no limit) */
ssize_t nchr = 0; /* number of chars actually read */
int *numbers = NULL; /* array to hold numbers */
size_t nmax = NMAX; /* check for reallocation */
size_t idx = 0; /* numbers array index */
if (!(numbers = calloc (NMAX, sizeof *numbers))) {
fprintf (stderr, "error: memory allocation failed.");
return 1;
}
/* read each line from stdin - dynamicallly allocated */
while ((nchr = getline (&ln, &n, stdin)) != -1)
{
char *p = ln; /* pointer for use with strtol */
char *ep = NULL;
errno = 0;
while (errno == 0)
{
/* parse/convert each number on stdin */
numbers[idx] = strtol (p, &ep, 10);
/* note: overflow/underflow checks omitted */
/* if valid conversion to number */
if (errno == 0 && p != ep)
{
idx++; /* increment index */
if (!ep) break; /* check for end of str */
}
/* skip delimiters/move pointer to next digit */
while (*ep && (*ep <= '0' || *ep >= '9')) ep++;
if (*ep)
p = ep;
else
break;
/* reallocate numbers if idx = nmax */
if (idx == nmax)
{
int *tmp = realloc (numbers, 2 * nmax * sizeof *numbers);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
exit (EXIT_FAILURE);
}
numbers = tmp;
memset (numbers + nmax, 0, nmax * sizeof *numbers);
nmax *= 2;
}
}
}
/* free mem allocated by getline */
if (ln) free (ln);
/* show values stored in array */
size_t i = 0;
for (i = 0; i < idx; i++)
printf (" numbers[%2zu] %d\n", i, numbers[i]);
/* free mem allocate to numbers */
if (numbers) free (numbers);
return 0;
}
输出
$ echo "3,5;6,7;8,9;11,4;;" | ./bin/prsistdin
numbers[ 0] 3
numbers[ 1] 5
numbers[ 2] 6
numbers[ 3] 7
numbers[ 4] 8
numbers[ 5] 11
numbers[ 6] 4
也适用于字符串存储在文件中的情况:
$ cat dat/numsemic.csv | ./bin/prsistdin
or
$ ./bin/prsistdin < dat/numsemic.csv
使用 fgets
且不使用 size_t
进行了一些修改,我很高兴删除了 getline
并替换了 fgets
。 getline
更灵活,为您处理 space 的分配,而 fgets
则由您决定。 (更不用说 getline
无需调用 strlen
即可返回实际读取的字符数)。
我的目标是保留读取任意长度行的能力以满足您的要求。这要么意味着最初分配一些巨大的行缓冲区(浪费),要么想出一个方案,在输入行缓冲区比 space 最初分配给 ln
长的情况下根据需要重新分配输入行缓冲区。 (这就是 getline
做得很好的地方)。我对结果相当满意。 注意: 我将重新分配代码放在函数中以保持 main
相当干净。 脚注 2
看看下面的代码。请注意,我在代码中保留了 DEBUG
预处理器指令,如果您想在每次分配时都将其吐出,则允许您使用 -DDEBUG
标志进行编译。 [脚注 1] 你可以编译代码:
gcc -Wall -Wextra -o yourexename yourfilename.c
或者如果您想要调试输出(例如将 LMAX 设置为 2
或小于行长度的值),请使用以下内容:
gcc -Wall -Wextra -o yourexename yourfilename.c -DDEBUG
如果您有任何问题,请告诉我:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define NMAX 256
#define LMAX 1024
char *realloc_char (char *sp, unsigned int *n); /* reallocate char array */
int *realloc_int (int *sp, unsigned int *n); /* reallocate int array */
char *fixshortread (FILE *fp, char **s, unsigned int *n); /* read all stdin */
int main () {
char *ln = NULL; /* dynamically allocated for fgets */
int *numbers = NULL; /* array to hold numbers */
unsigned int nmax = NMAX; /* numbers check for reallocation */
unsigned int lmax = LMAX; /* ln check for reallocation */
unsigned int idx = 0; /* numbers array index */
unsigned int i = 0; /* simple counter variable */
char *nl = NULL;
/* initial allocation for numbers */
if (!(numbers = calloc (NMAX, sizeof *numbers))) {
fprintf (stderr, "error: memory allocation failed (numbers).");
return 1;
}
/* initial allocation for ln */
if (!(ln = calloc (LMAX, sizeof *ln))) {
fprintf (stderr, "error: memory allocation failed (ln).");
return 1;
}
/* read each line from stdin - dynamicallly allocated */
while (fgets (ln, lmax, stdin) != NULL)
{
/* provide a fallback to read remainder of line
if the line length exceeds lmax */
if (!(nl = strchr (ln, '\n')))
fixshortread (stdin, &ln, &lmax);
else
*nl = 0;
char *p = ln; /* pointer for use with strtol */
char *ep = NULL;
errno = 0;
while (errno == 0)
{
/* parse/convert each number on stdin */
numbers[idx] = strtol (p, &ep, 10);
/* note: overflow/underflow checks omitted */
/* if valid conversion to number */
if (errno == 0 && p != ep)
{
idx++; /* increment index */
if (!ep) break; /* check for end of str */
}
/* skip delimiters/move pointer to next digit */
while (*ep && (*ep <= '0' || *ep >= '9')) ep++;
if (*ep)
p = ep;
else
break;
/* reallocate numbers if idx = nmax */
if (idx == nmax)
realloc_int (numbers, &nmax);
}
}
/* free mem allocated by getline */
if (ln) free (ln);
/* show values stored in array */
for (i = 0; i < idx; i++)
printf (" numbers[%2u] %d\n", (unsigned int)i, numbers[i]);
/* free mem allocate to numbers */
if (numbers) free (numbers);
return 0;
}
/* reallocate character pointer memory */
char *realloc_char (char *sp, unsigned int *n)
{
char *tmp = realloc (sp, 2 * *n * sizeof *sp);
#ifdef DEBUG
printf ("\n reallocating %u to %u\n", *n, *n * 2);
#endif
if (!tmp) {
fprintf (stderr, "Error: char pointer reallocation failure.\n");
exit (EXIT_FAILURE);
}
sp = tmp;
memset (sp + *n, 0, *n * sizeof *sp); /* memset new ptrs 0 */
*n *= 2;
return sp;
}
/* reallocate integer pointer memory */
int *realloc_int (int *sp, unsigned int *n)
{
int *tmp = realloc (sp, 2 * *n * sizeof *sp);
#ifdef DEBUG
printf ("\n reallocating %u to %u\n", *n, *n * 2);
#endif
if (!tmp) {
fprintf (stderr, "Error: int pointer reallocation failure.\n");
exit (EXIT_FAILURE);
}
sp = tmp;
memset (sp + *n, 0, *n * sizeof *sp); /* memset new ptrs 0 */
*n *= 2;
return sp;
}
/* if fgets fails to read entire line, fix short read */
char *fixshortread (FILE *fp, char **s, unsigned int *n)
{
unsigned int i = 0;
int c = 0;
i = *n - 1;
realloc_char (*s, n);
do
{
c = fgetc (fp);
(*s)[i] = c;
i++;
if (i == *n)
realloc_char (*s, n);
} while (c != '\n' && c != EOF);
(*s)[i-1] = 0;
return *s;
}
脚注 1
单词 DEBUG
的选择没什么特别的(它可能是 DOG
,等等),要点是如果你想有条件地 include/exclude代码,您可以简单地使用预处理器标志来执行此操作。您只需添加 -Dflagname
即可将 flagname
传递给编译器。
脚注 2
您可以将重新分配函数组合成一个单独的 void*
函数,该函数接受 void 指针作为其参数以及要重新分配的类型的 size
和 returns void指向重新分配的 space 的指针——但我们将把它留到以后再说。
如果我的程序要通过 stdin
传入大量数字列表,那么最有效的读取方法是什么?
我要传递给程序的输入将采用以下格式:
3,5;6,7;8,9;11,4;;
我需要处理输入,以便我可以使用冒号之间的数字(即我希望能够使用 3 和 5、6 和 7 等)。 ;;
表示它是行尾。
我正在考虑使用缓冲 reader 读取整行然后使用 parseInt。
这是最有效的方法吗?
我对 C 有点生疏,但这对你有用吗?
char[1000] remainder;
int first, second;
fp = fopen("C:\file.txt", "r"); // Error check this, probably.
while (fgets(&remainder, 1000, fp) != null) { // Get a line.
while (sscanf(remainder, "%d,%d;%s", first, second, remainder) != null) {
// place first and second into a struct or something
}
}
您可以使用 fgets or fgetc. You could also use getline() 从 stdin 读取,因为您正在从 stdin 读取。
读入该行后,您可以使用 strtok() with the delimiter for ";" to split the string into pieces at the semicolons. You can loop through until strok() is null, or in this case, ';'. Also in C you should use atoi() 将字符串转换为整数。
例如:
int length = 256;
char* str = (char*)malloc(length);
int err = getline(&str, &length, stdin);
我会读入命令参数,然后使用 strtok()
库方法
http://man7.org/linux/man-pages/man3/strtok.3.html
(上面URL引用的网页还有使用方法的代码示例。)
这是一个可行的解决方案
一种方法是使用 strtok() 并将值存储在数组中。理想情况下,动态分配。
int main(int argc, char *argv[])
{
int lst_size=100;
int line_size=255;
int lst[lst_size];
int count=0;
char buff[line_size];
char * token=NULL;
fgets (buff, line_size, stdin); //Get input
通过传递“,”和“;”来使用 strtok作为分隔符。
token=strtok(buff, ";,");
lst[count++]=atoi(token);
while(token=strtok(NULL, ";,")){
lst[count++]=atoi(token);
}
最后你必须考虑双“;;”通过将计数减 1,因为对于这种情况 atoi(token) 将 return 0 并将其存储在第 n 个索引中。你不想要的。
count--;
}
getchar_unlocked()
就是您要找的。
代码如下:
#include <stdio.h>
inline int fastRead_int(int * x)
{
register int c = getchar_unlocked();
*x = 0;
// clean stuff in front of + look for EOF
for(; ((c<48 || c>57) && c != EOF); c = getchar_unlocked());
if(c == EOF)
return 0;
// build int
for(; c>47 && c<58 ; c = getchar_unlocked()) {
*x = (*x<<1) + (*x<<3) + c - 48;
}
return 1;
}
int main()
{
int x;
while(fastRead_int(&x))
printf("%d ",x);
return 0;
}
对于输入 1;2;2;;3;;4;;;;;54;;;;
上面的代码生成 1 2 2 3 4 54
.
我保证,这个解决方案比本主题中介绍的其他解决方案快得多。它不仅使用getchar_unlocked()
,还使用register
、inline
以及乘以10的技巧:(*x<<1) + (*x<<3)
.
祝你好运,找到更好的解决方案。
另一种相当优雅的处理方法是允许 strtol
通过将要读取的字符串推进到 strtol
返回的 endptr
来解析输入。根据需要结合数组 allocated/reallocated ,您应该能够处理任何长度的行(直到内存耗尽)。下面的示例使用单个数组作为数据。如果你想存储多行,每行作为一个单独的数组,你可以使用相同的方法,但从指向 int 指针数组的指针开始。 (即 int **numbers
并分配指针,然后分配每个数组)。如果您有任何问题,请告诉我:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define NMAX 256
int main () {
char *ln = NULL; /* NULL forces getline to allocate */
size_t n = 0; /* max chars to read (0 - no limit) */
ssize_t nchr = 0; /* number of chars actually read */
int *numbers = NULL; /* array to hold numbers */
size_t nmax = NMAX; /* check for reallocation */
size_t idx = 0; /* numbers array index */
if (!(numbers = calloc (NMAX, sizeof *numbers))) {
fprintf (stderr, "error: memory allocation failed.");
return 1;
}
/* read each line from stdin - dynamicallly allocated */
while ((nchr = getline (&ln, &n, stdin)) != -1)
{
char *p = ln; /* pointer for use with strtol */
char *ep = NULL;
errno = 0;
while (errno == 0)
{
/* parse/convert each number on stdin */
numbers[idx] = strtol (p, &ep, 10);
/* note: overflow/underflow checks omitted */
/* if valid conversion to number */
if (errno == 0 && p != ep)
{
idx++; /* increment index */
if (!ep) break; /* check for end of str */
}
/* skip delimiters/move pointer to next digit */
while (*ep && (*ep <= '0' || *ep >= '9')) ep++;
if (*ep)
p = ep;
else
break;
/* reallocate numbers if idx = nmax */
if (idx == nmax)
{
int *tmp = realloc (numbers, 2 * nmax * sizeof *numbers);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
exit (EXIT_FAILURE);
}
numbers = tmp;
memset (numbers + nmax, 0, nmax * sizeof *numbers);
nmax *= 2;
}
}
}
/* free mem allocated by getline */
if (ln) free (ln);
/* show values stored in array */
size_t i = 0;
for (i = 0; i < idx; i++)
printf (" numbers[%2zu] %d\n", i, numbers[i]);
/* free mem allocate to numbers */
if (numbers) free (numbers);
return 0;
}
输出
$ echo "3,5;6,7;8,9;11,4;;" | ./bin/prsistdin
numbers[ 0] 3
numbers[ 1] 5
numbers[ 2] 6
numbers[ 3] 7
numbers[ 4] 8
numbers[ 5] 11
numbers[ 6] 4
也适用于字符串存储在文件中的情况:
$ cat dat/numsemic.csv | ./bin/prsistdin
or
$ ./bin/prsistdin < dat/numsemic.csv
使用 fgets
且不使用 size_t
进行了一些修改,我很高兴删除了 getline
并替换了 fgets
。 getline
更灵活,为您处理 space 的分配,而 fgets
则由您决定。 (更不用说 getline
无需调用 strlen
即可返回实际读取的字符数)。
我的目标是保留读取任意长度行的能力以满足您的要求。这要么意味着最初分配一些巨大的行缓冲区(浪费),要么想出一个方案,在输入行缓冲区比 space 最初分配给 ln
长的情况下根据需要重新分配输入行缓冲区。 (这就是 getline
做得很好的地方)。我对结果相当满意。 注意: 我将重新分配代码放在函数中以保持 main
相当干净。 脚注 2
看看下面的代码。请注意,我在代码中保留了 DEBUG
预处理器指令,如果您想在每次分配时都将其吐出,则允许您使用 -DDEBUG
标志进行编译。 [脚注 1] 你可以编译代码:
gcc -Wall -Wextra -o yourexename yourfilename.c
或者如果您想要调试输出(例如将 LMAX 设置为 2
或小于行长度的值),请使用以下内容:
gcc -Wall -Wextra -o yourexename yourfilename.c -DDEBUG
如果您有任何问题,请告诉我:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define NMAX 256
#define LMAX 1024
char *realloc_char (char *sp, unsigned int *n); /* reallocate char array */
int *realloc_int (int *sp, unsigned int *n); /* reallocate int array */
char *fixshortread (FILE *fp, char **s, unsigned int *n); /* read all stdin */
int main () {
char *ln = NULL; /* dynamically allocated for fgets */
int *numbers = NULL; /* array to hold numbers */
unsigned int nmax = NMAX; /* numbers check for reallocation */
unsigned int lmax = LMAX; /* ln check for reallocation */
unsigned int idx = 0; /* numbers array index */
unsigned int i = 0; /* simple counter variable */
char *nl = NULL;
/* initial allocation for numbers */
if (!(numbers = calloc (NMAX, sizeof *numbers))) {
fprintf (stderr, "error: memory allocation failed (numbers).");
return 1;
}
/* initial allocation for ln */
if (!(ln = calloc (LMAX, sizeof *ln))) {
fprintf (stderr, "error: memory allocation failed (ln).");
return 1;
}
/* read each line from stdin - dynamicallly allocated */
while (fgets (ln, lmax, stdin) != NULL)
{
/* provide a fallback to read remainder of line
if the line length exceeds lmax */
if (!(nl = strchr (ln, '\n')))
fixshortread (stdin, &ln, &lmax);
else
*nl = 0;
char *p = ln; /* pointer for use with strtol */
char *ep = NULL;
errno = 0;
while (errno == 0)
{
/* parse/convert each number on stdin */
numbers[idx] = strtol (p, &ep, 10);
/* note: overflow/underflow checks omitted */
/* if valid conversion to number */
if (errno == 0 && p != ep)
{
idx++; /* increment index */
if (!ep) break; /* check for end of str */
}
/* skip delimiters/move pointer to next digit */
while (*ep && (*ep <= '0' || *ep >= '9')) ep++;
if (*ep)
p = ep;
else
break;
/* reallocate numbers if idx = nmax */
if (idx == nmax)
realloc_int (numbers, &nmax);
}
}
/* free mem allocated by getline */
if (ln) free (ln);
/* show values stored in array */
for (i = 0; i < idx; i++)
printf (" numbers[%2u] %d\n", (unsigned int)i, numbers[i]);
/* free mem allocate to numbers */
if (numbers) free (numbers);
return 0;
}
/* reallocate character pointer memory */
char *realloc_char (char *sp, unsigned int *n)
{
char *tmp = realloc (sp, 2 * *n * sizeof *sp);
#ifdef DEBUG
printf ("\n reallocating %u to %u\n", *n, *n * 2);
#endif
if (!tmp) {
fprintf (stderr, "Error: char pointer reallocation failure.\n");
exit (EXIT_FAILURE);
}
sp = tmp;
memset (sp + *n, 0, *n * sizeof *sp); /* memset new ptrs 0 */
*n *= 2;
return sp;
}
/* reallocate integer pointer memory */
int *realloc_int (int *sp, unsigned int *n)
{
int *tmp = realloc (sp, 2 * *n * sizeof *sp);
#ifdef DEBUG
printf ("\n reallocating %u to %u\n", *n, *n * 2);
#endif
if (!tmp) {
fprintf (stderr, "Error: int pointer reallocation failure.\n");
exit (EXIT_FAILURE);
}
sp = tmp;
memset (sp + *n, 0, *n * sizeof *sp); /* memset new ptrs 0 */
*n *= 2;
return sp;
}
/* if fgets fails to read entire line, fix short read */
char *fixshortread (FILE *fp, char **s, unsigned int *n)
{
unsigned int i = 0;
int c = 0;
i = *n - 1;
realloc_char (*s, n);
do
{
c = fgetc (fp);
(*s)[i] = c;
i++;
if (i == *n)
realloc_char (*s, n);
} while (c != '\n' && c != EOF);
(*s)[i-1] = 0;
return *s;
}
脚注 1
单词 DEBUG
的选择没什么特别的(它可能是 DOG
,等等),要点是如果你想有条件地 include/exclude代码,您可以简单地使用预处理器标志来执行此操作。您只需添加 -Dflagname
即可将 flagname
传递给编译器。
脚注 2
您可以将重新分配函数组合成一个单独的 void*
函数,该函数接受 void 指针作为其参数以及要重新分配的类型的 size
和 returns void指向重新分配的 space 的指针——但我们将把它留到以后再说。