从C中的命令行捕获可变长度字符串

我到处寻找我的问题的答案,但我还没有找到一个可靠的答案来解决我的问题。

我目前正在用C编写程序,专门针对UNIX命令行(我使用Linux作为我的开发环境,但我希望这个程序尽可能便携)。 现在,我有一个基本的shell,提示用户输入。 然后,用户将输入命令,并相应地处理该命令。 这是我到目前为止的代码:

/* Main.c */ int main(int argc, char **argv) { while (TRUE) { display_prompt(); get_command(); } return 0; } /* Main.h */ void get_command() { /* * Reads in a command from the user, outputting the correct response */ int buffer_size = 20; char *command = (char*) malloc(sizeof(char) * buffer_size); if (command == NULL) { return_error("Error allocating memory"); } fgets(command, buffer_size, stdin); if (command[strlen(command) - 1] == '\n') { puts("It's inside the buffer."); } else { puts("It's not inside the buffer."); } free(command); } 

我最初的想法是检查\n字符并查看它是否适合buffer_size ,如果它没有realloc()数据以扩展分配的内存。

但是,在我realloc()我的字符串之后,我如何将stdin的剩余数据添加到command

如果你真的需要,请使用getline(3)。 这是POSIX.1-2008。 请注意,无限长度行是DOS攻击(OOM)的简单攻击向量。 因此,考虑制定合理的行长度限制,并使用fgets(3)。

我认为关于假设最大命令长度的答案是正确的:通常你会希望将命令保持在合理的长度内。

但如果你真的不能对命令的最大长度做出假设,那么你需要缓冲。

保持:

  • 一个固定的buffer ,你总是将相同数量的字符放入,和
  • 您追加的command ,并在必要时重新分配。

以下代码可能缺少一些error handling:

 #define BUFFER_SIZE 20 #define COMMAND_BLOCK_SIZE 50 void get_command() { char *buffer = malloc(sizeof(char) * (BUFFER_SIZE + 1)); char *command = malloc(sizeof(char) * (COMMAND_BLOCK_SIZE + 1)); int commandSize = 50; // tmp pointer for realloc: char *tmp = NULL; char *retval = NULL; if ((buffer == NULL) || (command == NULL)) return_error("Error allocating memory"); retval = fgets(buffer, BUFFER_SIZE, stdin); while (retval != NULL) { if (strlen(buffer) + strlen(command) > commandSize) { tmp = realloc(command, commandSize + (COMMAND_BLOCK_SIZE + 1)); if (tmp == NULL) return_error("Error allocating memory"); else { command = tmp; commandSize += COMMAND_BLOCK_SIZE; } } // not using strncat because the check above should guarantee that // we always have more than BUFFER_SIZE more bytes in command strcat(command, buffer); if (buffer[strlen(buffer) - 1] == '\n') break; retval = fgets(buffer, BUFFER_SIZE, stdin); } printf("COMMAND: %s\n", command); free(buffer); } 

另请注意:

  • 我们没有对那里的command做任何有用的事情,你可能想传入一个char **这样你就可以从这个函数中获取command ,并在调用代码中释放它,例如在你的主循环中。
  • ‘\ n’保留在command :您可能想要丢弃它。

你不需要做任何realloc,只需要为\ 0添加超过最大命令长度1个字节并忘记\ n因为你不会得到\ n总是用户输入。 如果用户输入超过了长度,那么你的字符串将在没有\ n的情况下被截断。 所以fgets后你的情况不正确并且基于错误的假设。

就像是:

  int buffer_size = MAX_COMMAND_LENGTH + 1; 

关于内存分配:在这种情况下,你应该使用堆栈而不是堆避免malloc / free。 因此,您的代码将更简单,更不容易出错:

  char command[buffer_size]; ... // free(command) <-- you dont need this anymore 

请注意,函数返回后将释放您的命令。 因此,如果你将它处理成get_command就可以了,但是如果你想将它返回给调用者你将从调用者那里收到一个缓冲区。

如果您使用的是gnu系统,请使用c库的gnu getline扩展 ,它会为您完成所有动态大小调整。

例如(虽然我没有测试过)

 void get_command() { /* * Reads in a command from the user, outputting the correct response */ size_t buffer_size = 0; char *command = NULL; ssize_t len = getline(&command, &buffer_size, stdin); if(len < 0) { perror("Error reading input"); } else if (command[len - 1] == '\n') { puts("It's inside the buffer."); } else { puts("It's not inside the buffer."); } free(command); }