扫描长度限制的字符串

时间:2014-09-16 15:08:14

标签: c

使用标准C库,只有在符合缓冲区的情况下才能从标准输入中扫描字符串(不包含空格)吗?在以下示例中,如果输入字符串大于32,我希望 scanCount 为0:

char str[32];
int scanCount;

scanCount = scanf("%32s", str);

编辑:当输入字符串太大时,我还需要文件指针回滚。

4 个答案:

答案 0 :(得分:1)

您需要在扫描失败时重置文件指针的注释使得scanf()无法做到这一点。

scanf()基本上指定为“fscanf( stdin, ... )”,fscanf() 已定义以“[推送]最多一个输入字符返回到输入流“(C99,脚注242)。 (我假设这是出于同样的原因,ungetc()只需要支持一个字节的回推:这样它就可以方便地缓存在内存中。)

*scanf()是读取不确定输入的不良选择,原因如上所述,以及从错误中恢复时的其他一些缺点。一般来说,如果任何机会输入可能不符合预期格式,请先将输入读入内部存储器缓冲区,然后再从中解析。

答案 1 :(得分:1)

您指定的要求是仅读取整个数据是否适合您的缓冲区。此要求完全没有意义,因为它不为您的程序提供任何功能。没有它,您可以轻松实现相同类型的任务。操作系统也不是如何向用户应用程序提供文件。

您可以简单地创建一个您认为合适的任何大小的缓冲区,然后您可以将数据保存在缓冲区中,直到您可以处理它,或者您可以像实际调整缓冲区一样来调整缓冲区以容纳更多的传入数据。

您可以使用ANSI fread()函数从文件中读取任意数量的字符:

size_t count;
char buffer[50];

count = fread(buffer, 1, sizeof buffer, stdin);

然后,您可以通过查看count变量来查看实际读取的字符数,如果小于缓冲区大小,您可以填写最终的NUL字符,或者您可以决定如果已读取整个缓冲区并且可能有更多数据,则执行下一步操作。您当然可以阅读sizeof buffer - 1,以便能够始终完成字符串。当计数小于指定值时,feof()ferror()可用于查看发生的情况。您还可以查看实际情况并检查LF字符以查看您已阅读的行数。

使用放大缓冲区时,您需要malloc()或只需创建一个空指针,稍后将使用realloc()分配:

/* Set initial size and offset. */
size_t offset = 0;
size_t size = 0;
char *buffer = NULL;

当您需要更改缓冲区的大小时,可以使用realloc()

/* Change the size. */
size = 100;
buffer = realloc(buffer, size);

(第一次相当于buffer = malloc(size)。)

然后,您可以将数据读入缓冲区:

size_t count = fread(buffer + offset, 1, size - offset, stdin);
count += offset;

(第一次相当于fread(buffer, 1, size, stdin)。)

完成后,你应该释放缓冲区:

free(buffer);

在任何时候,您仍然可以在缓冲区中的某个位置读取所有已读取的数据,因此您可以随时返回它,只需将读取和处理分离,上面的示例都是关于读取的

然后处理取决于您的需求。您通常需要确定要提取的数据的开头和结尾。

示例开始和结束,其中end表示最后一个字符后面的一个字符,因此算术效果更好:

size_t start = 0;
size_t end = 10;

提取数据(使用C99位):

char data[end - start + 1];
memcpy(data, buffer + start, end - start);
data[end] = '\0';

现在您有一个以NUL结尾的字符串,其中包含您要提取的数据。有时您只是假设start = 0,然后想要使用缓冲区中的数据来为新数据做好准备:

char data[end + 1];
/* copy out the data */
memcpy(data, buffer, end);
/* move data between end end offset to the beginning */
memmove(buffer, buffer + end, offset - end);
/* adjust the offset accordingly */
offset -= end;

现在您已经提取了数据,但仍然可以使用尚未处理的其余数据准备好缓冲区。这有效地实现了您的目标,例如通过将数据保存在中间缓冲区中,您可以有效地窥视输入中接收到的数据的任意部分,并且只有在符合您的期望时才会取出数据,如果他们不喜欢,则执行其他任何操作当然,你应该仔细测试所有的返回值,以检查异常情况和这些东西。

我个人也会将示例中的所有索引直接转换为内存指针并相应地调整算法,但不是每个人都像我一样喜欢指针算术;)。我也倾向于在ANSI API的形式中使用低级别的POSIX API而不是intermetiate层。准备修复错误或改进解释,请发表评论。

答案 2 :(得分:0)

只需阅读并存储一个字符过多,然后对其进行测试。

char str[34];    // 33 characters + NUL terminator
int scanCount = scanf("%33s", str);
if (scanCount > 0 && strlen(str) > 32)
{
    scanCount = 0;
}

答案 3 :(得分:0)

扫描诸如stdin之类的流只允许"放回"最多1 char。因此,扫描32或33 char然后撤消是不可能的。

如果您的输入可以使用ftell()fseek()(重定向stdin时可用),则代码可以

long pos = ftell(input);
char str[32+1];
int scanCount;
scanCount = fscanf(input, "%32s", str);
if (scanCount != 1 || strlen(str) >= 32) {
  fseek(input, pos, SEEK_SET);
  scanCount = fscanf(input, some_new_format, ....); 
}

否则,请使用fgets()读取最大行并使用sscanf()

char buf[1024];
if (fget(buf, sizeof buf, stdin) == NULL) Handle_IOError_or_EOF();

char str[32+1];
int scanCount;
scanCount = sscanf(buf, "%32s", str);
if (scanCount != 1 || strlen(str) >= 32) {
  scanCount = sscanf(buf, some_new_format, ....); 
}