C: strncpy 意外截断字符串
C: strncpy unexpectedly truncating string
我正在逐行读取文件,其中每一行的格式为:
" 数字 1 \t 数字 2".
我正在使用 strtok 和 strncpy 拆分然后根据需要存储这两个值。
但是,我发现在strncpy之后,number1被截断了一位。
知道为什么会这样以及如何解决吗?
为简单起见,我硬编码了一个line_of_text来模拟这个问题。
谢谢!
代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char line_of_text[80] = " 18306000 \t 100\n";
// NB: There's a \t between the two values, the rest are spaces.
char* token;
int digits_per_num = 9;
char first_token[digits_per_num];
char second_token[digits_per_num];
token = strtok (line_of_text, "\t");
printf("first token: %s\n", token);
if (token != NULL)
{
strncpy (first_token, token, digits_per_num);
}
token = strtok (NULL, ",\t\n");
if (token != NULL)
{
strncpy (second_token, token, digits_per_num);
}
printf("first token copy: %s\n", first_token);
printf("second token copy: %s\n", second_token);
}
输出:
first token: 18306000
first token copy: 1830600<junk>
second token copy: 100
第一个标记由 10 个字节组成:18306000[=11=]
。
strncpy()
仅写入适合目标缓冲区的空字符。但是你分配的字符太少了所以没有。
最简单的解决方法是在两个 strtok 调用的分隔符中包含空格:
token = strtok (line_of_text, " \t\n,");
我还建议使用 snprintf()
而不是 strncpy
,这样您就可以始终保证在字符串末尾得到一个空字符。
问题是缓冲区不够大,无法容纳字符串;在这种情况下,strncpy
函数不会以 null 终止缓冲区。
仅仅像您在评论中建议的那样增加缓冲区大小并不是一个稳健的解决方案,因为如果有人提供不同的输入和更长的数字,同样的问题会再次出现。
一种选择是手动终止缓冲区:
strncpy(first_token, token, digits_per_num);
first_token[digits_per_num - 1] = 0;
(注意:使用 sizeof first_token
而不是 digits_per_num
也会更健壮)。
但是在这种情况下,无效输入由静默截断处理。如果这不适合您的程序,那么您可以使用不同的逻辑,并完全避免不直观的 strncpy
函数:
if ( strlen(token) + 1 > digits_per_num )
{
fprintf(stderr, "Error, input was more than 9 digits\n");
exit(EXIT_FAILURE);
}
strcpy(first_token, token);
当您之前检查过长度时,使用 strcpy
是安全的。
我正在逐行读取文件,其中每一行的格式为:
" 数字 1 \t 数字 2".
我正在使用 strtok 和 strncpy 拆分然后根据需要存储这两个值。 但是,我发现在strncpy之后,number1被截断了一位。
知道为什么会这样以及如何解决吗?
为简单起见,我硬编码了一个line_of_text来模拟这个问题。
谢谢!
代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char line_of_text[80] = " 18306000 \t 100\n";
// NB: There's a \t between the two values, the rest are spaces.
char* token;
int digits_per_num = 9;
char first_token[digits_per_num];
char second_token[digits_per_num];
token = strtok (line_of_text, "\t");
printf("first token: %s\n", token);
if (token != NULL)
{
strncpy (first_token, token, digits_per_num);
}
token = strtok (NULL, ",\t\n");
if (token != NULL)
{
strncpy (second_token, token, digits_per_num);
}
printf("first token copy: %s\n", first_token);
printf("second token copy: %s\n", second_token);
}
输出:
first token: 18306000
first token copy: 1830600<junk>
second token copy: 100
第一个标记由 10 个字节组成:18306000[=11=]
。
strncpy()
仅写入适合目标缓冲区的空字符。但是你分配的字符太少了所以没有。
最简单的解决方法是在两个 strtok 调用的分隔符中包含空格:
token = strtok (line_of_text, " \t\n,");
我还建议使用 snprintf()
而不是 strncpy
,这样您就可以始终保证在字符串末尾得到一个空字符。
问题是缓冲区不够大,无法容纳字符串;在这种情况下,strncpy
函数不会以 null 终止缓冲区。
仅仅像您在评论中建议的那样增加缓冲区大小并不是一个稳健的解决方案,因为如果有人提供不同的输入和更长的数字,同样的问题会再次出现。
一种选择是手动终止缓冲区:
strncpy(first_token, token, digits_per_num);
first_token[digits_per_num - 1] = 0;
(注意:使用 sizeof first_token
而不是 digits_per_num
也会更健壮)。
但是在这种情况下,无效输入由静默截断处理。如果这不适合您的程序,那么您可以使用不同的逻辑,并完全避免不直观的 strncpy
函数:
if ( strlen(token) + 1 > digits_per_num )
{
fprintf(stderr, "Error, input was more than 9 digits\n");
exit(EXIT_FAILURE);
}
strcpy(first_token, token);
当您之前检查过长度时,使用 strcpy
是安全的。