我认为可以使用一个适当大的中间缓冲区,并通过限制字符串长度为最大缓冲区大小,使用fgets
或其他函数将其输入到其中。稍后当字符串被输入时,计算字符串长度并分配一个与字符串大小相同的缓冲区,并将其复制到新分配的缓冲区中。旧的大缓冲区可以重复利用。
你可以这样做:
fgets (buffer, BUFSIZ, stdin);
或者
scanf ("%128[^\n]%*c", buffer);
在这里,你可以指定缓冲区长度为128字节,如%128..
,并且包括字符串中的所有空格。
然后计算长度并分配新的缓冲区:
len = strlen (buffer);
string = malloc (sizeof (char) * len + 1);
strcpy (string, buffer);
.
.
.
free (string);
编辑
以下是我找到的一种方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void)
{
char *buffer[10]; /* temporary buffers 10 nos, or make this dynamically allocated */
char *main_str; /* The main string to work with after input */
int k, i=0, n, retval;
while (1)
{
buffer[i] = malloc (sizeof (char) * 16); /* allocate buffer size 16 */
scanf ("%15[^\n]%n", buffer[i], &n); /* input length 15 string + 1 byte for null */
if (n<16) /* Buffer is not filled and end of string reached */
break;
n=0; /* reinitialize n=0 for next iteration. to make the process work if the length of the string is exactly the sizeof the buffer */
i++;
}
/* need to fix the while loop so that the buffer array does not overflow and protect it from doing so */
/* allocate buffer of exact size of the string */
main_str = malloc (sizeof (char) * 16 * i + strlen (buffer[i]));
/* copy the segmented string into the main string to be worked with
* and free the buffers
*/
strcpy (main_str, "");
for (k=0; k<=i; k++)
{
strcat (main_str, buffer[k]);
free (buffer[k]);
}
/* work with main string */
printf ("\n%s", main_str);
/* free main string */
free (main_str);
return 0;
}
您需要修复代码以防止某些情况下崩溃,但这应该可以回答您的问题。
gets()
。请使用fgets()
,并大致估计所需的缓冲空间。
fgets
的优点是,如果您超出了缓冲区大小,它只会写入最大数量的字符,并且不会破坏程序的其他部分的内存。char buff[100];
fgets(buff,100,stdin);
将只读取最多99个字符或直到遇到`'\n'`为止。如果有空间,它将把换行符读入数组中。
动态分配缓冲区并使用fgets。如果您将缓冲区填满,则它不够大,请使用realloc进行扩展,然后再次使用fgets(但要写入字符串的末尾以保留已经获取的内容)。一直重复此过程,直到您的缓冲区大于输入为止:
buffer = malloc(bufsize);
do{
GotStuff = fgets(buffer, bufsize, stdin))
buffer[bufsize-1] = 0;
if (GotStuff && (strlen(buffer) >= bufsize-1))
{
oldsize = bufsize;
buffer = realloc(bufsize *= 2);
GotStuff = fgets( buffer + oldsize, bufsize - oldsize, stdin )
buffer[bufsize-1] = 0;
}
} while (GotStuff && (strlen(buffer) >= bufsize-1));
不要使用gets()
。改用fgets()
。
不能安全地使用gets()
获取用户输入。
您需要在循环中使用fgets()
(或fgetc()
)。
gets()
函数将在下一个C标准中被弃用。 - ninjaljrealloc
。 - pmggets()
函数的问题 - 没有办法知道目标缓冲区需要多大来存储输入 - 正是该库调用在1999年标准中被弃用的原因,预计它将完全从下一个版本中消失;预计大多数编译器会相对迅速地跟进。由那个库函数引起的混乱比破坏40年遗留代码的前景更可怕。fgets()
和固定长度缓冲区逐块读取输入,然后将其附加到动态可调整大小的目标缓冲区中。例如:#include <stdio.h>
#include <stdlib.h>
#define SIZE 512;
char *getNextLine(FILE *stream, size_t *length)
{
char *output;
char input[SIZE+1];
*length = 0;
int foundNewline = 0;
/**
* Initialize our output buffer
*/
if ((output = malloc(1)) != NULL);
{
*output = 0;
*length = 1;
}
else
{
return NULL;
}
/**
* Read SIZE chars from the input stream until we hit EOF or
* see a newline character
*/
while(fgets(input, sizeof input, stream) != NULL && !foundNewline)
{
char *newline = strchr(input, '\n');
char *tmp = NULL;
/**
* Strip the newline if present
*/
foundNewline = (newline != NULL);
if (foundNewline)
{
*newline = 0;
}
/**
* Extend the output buffer
*/
tmp = realloc(output, *length + strlen(input));
if (tmp)
{
output = tmp;
strcat(output, input);
*length += strlen(input);
}
}
return *output;
}
如果您使用的是Unix平台,您应该使用getline()
,它专门用于这种情况。
如果您的平台没有getline()
,那么这里有一些公共领域代码,可以让您使用它。这篇文章有点长,但这是因为代码试图实际处理现实生活中的错误和情况(甚至是不太真实的情况,比如内存耗尽)。
它可能不是最高效的版本或最优雅的版本。它使用fgetc()
逐个拾取字符,并在读取字符时每次都将空终止符放在数据末尾。但是,我认为即使面对错误和大量或少量的数据,它也是正确的。它对我的目的来说表现得足够好。
我并不特别喜欢getline()
接口,但我使用它是因为它是某种标准。
以下内容将与GCC(MinGW)和MSVC一起编译(作为C++ - 它使用声明与语句混合,当作为C编译时,MSVC仍不支持。也许有一天我会修复它)。
#define _CRT_SECURE_NO_WARNINGS 1
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#if !__GNUC__
#if _WIN64
typedef long long ssize_t;
#else
typedef long ssize_t;
#endif
#endif
#if !defined(SSIZE_MAX)
#define SSIZE_MAX ((ssize_t)(SIZE_MAX/2))
#endif
#if !defined(EOVERFLOW)
#define EOVERFLOW (ERANGE) /* is there something better to use? */
#endif
ssize_t nx_getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
ssize_t nx_getline(char **lineptr, size_t *n, FILE *stream);
/*
nx_getdelim_get_realloc_size()
Helper function for getdelim() to figure out an appropriate new
allocation size that's not too small or too big.
These numbers seem to work pretty well for most text files.
returns the input value if it decides that new allocation block
would be too big (the caller should handle this as
an error).
*/
static
size_t nx_getdelim_get_realloc_size( size_t current_size)
{
enum {
k_min_realloc_inc = 32,
k_max_realloc_inc = 1024,
};
if (SSIZE_MAX < current_size) return current_size;
if (current_size <= k_min_realloc_inc) return current_size + k_min_realloc_inc;
if (current_size >= k_max_realloc_inc) return current_size + k_max_realloc_inc;
return current_size * 2;
}
/*
nx_getdelim_append()
a helper function for getdelim() that adds a new character to
the outbuffer, reallocating as necessary to ensure the character
and a following null terminator can fit
*/
static
int nx_getdelim_append( char** lineptr, size_t* bufsize, size_t count, char ch)
{
char* tmp = NULL;
size_t tmp_size = 0;
// assert the contracts for this functions inputs
assert( lineptr != NULL);
assert( bufsize != NULL);
if (count >= (((size_t) SSIZE_MAX) + 1)) {
// writing more than SSIZE_MAX to the buffer isn't supported
return -1;
}
tmp = *lineptr;
tmp_size = tmp ? *bufsize : 0;
// need room for the character plus the null terminator
if ((count + 2) > tmp_size) {
tmp_size = nx_getdelim_get_realloc_size( tmp_size);
tmp = (char*) realloc( tmp, tmp_size);
if (!tmp) {
return -1;
}
}
*lineptr = tmp;
*bufsize = tmp_size;
// remember, the reallocation size calculation might not have
// changed the block size, so we have to check again
if (tmp && ((count+2) <= tmp_size)) {
tmp[count++] = ch;
tmp[count] = 0;
return 1;
}
return -1;
}
/*
nx_getdelim()
A getdelim() function modeled on the Linux/POSIX/GNU
function of the same name.
Read data into a dynamically resizable buffer until
EOF or until a delimiter character is found. The returned
data will be null terminated (unless there's an error
that prevents it).
params:
lineptr - a pointer to a char* allocated by malloc()
(actually any pointer that can legitimately be
passed to free()). *lineptr will be updated
by getdelim() if the memory block needs to be
reallocated to accommodate the input data.
*lineptr can be NULL (though lineptr itself cannot),
in which case the function will allocate any necessary
buffer.
n - a pointer to a size_t object that contains the size of
the buffer pointed to by *lineptr (if non-NULL).
The size of whatever buff the resulting data is
returned in will be passed back in *n
delim - the delimiter character. The function will stop
reading one this character is read form the stream.
It will be included in the returned data, and a
null terminator character will follow it.
stream - A FILE* stream object to read data from.
Returns:
The number of characters placed in the returned buffer, including
the delimiter character, but not including the terminating null.
If no characters are read and EOF is set (or attempting to read
from the stream on the first attempt caused the eof indication
to be set), a null terminator will be written to the buffer and
0 will be returned.
If an error occurs while reading the stream, a 0 will be returned.
A null terminator will not necessarily be at the end of the data
written.
On the following error conditions, the negative value of the error
code will be returned:
ENOMEM: out of memory
EOVERFLOW: SSIZE_MAX character written to te buffer before
reaching the delimiter
(on Windows, EOVERFLOW is mapped to ERANGE)
The buffer will not necessarily be null terminated in these cases.
Notes:
The returned data might include embedded nulls (if they exist
in the data stream) - in that case, the return value of the
function is the only way to reliably determine how much data
was placed in the buffer.
If the function returns 0 use feof() and/or ferror() to determine
which case caused the return.
If EOF is returned after having written one or more characters
to the buffer, a normal count will be returned (but there will
be no delimiter character in the buffer).
If 0 is returned and ferror() returns a non-zero value,
the data buffer may not be null terminated.
In other cases where a negative value is returned, the data
buffer is not necessarily null terminated and there
is no reliable means to determining what data in the buffer is
valid.
The pointer returned in *lineptr and the buffer size
returned in *n will be valid on error returns unless
NULL pointers are passed in for one or more of these
parameters (in which case the return value will be -EINVAL).
*/
ssize_t nx_getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
{
int retval = 0;
if (!lineptr || !n) {
return -EINVAL;
}
ssize_t result = 0;
char* line = *lineptr;
size_t size = *n;
size_t count = 0;
int err = 0;
int ch;
for (;;) {
ch = fgetc( stream);
if (ch == EOF) {
break;
}
result = nx_getdelim_append( &line, &size, count, ch);
// check for error adding to the buffer (ie., out of memory)
if (result < 0) {
err = -ENOMEM;
break;
}
++count;
// check if we're done because we've found the delimiter
if ((unsigned char)ch == (unsigned char)delim) {
break;
}
// check if we're passing the maximum supported buffer size
if (count > SSIZE_MAX) {
err = -EOVERFLOW;
break;
}
}
// update the caller's data
*lineptr = line;
*n = size;
// check for various error returns
if (err != 0) {
return err;
}
if (ferror(stream)) {
return 0;
}
if (feof(stream) && (count == 0)) {
if (nx_getdelim_append( &line, &size, count, 0) < 0) {
return -ENOMEM;
}
}
return count;
}
ssize_t nx_getline(char **lineptr, size_t *n, FILE *stream)
{
return nx_getdelim( lineptr, n, '\n', stream);
}
/*
versions of getline() and getdelim() that attempt to follow
POSIX semantics (ie. they set errno on error returns and
return -1 when the stream error indicator or end-of-file
indicator is set (ie., ferror() or feof() would return
non-zero).
*/
ssize_t getdelim(char **lineptr, size_t *n, char delim, FILE *stream)
{
ssize_t retval = nx_getdelim( lineptr, n, delim, stream);
if (retval < 0) {
errno = -retval;
retval = -1;
}
if (retval == 0) {
retval = -1;
}
return retval;
}
ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
return getdelim( lineptr, n, '\n', stream);
}