主要情况下,我将一个字符串传递给应该分隔字符串的不同函数,然后使用每个子字符串。在这种情况下,我需要获取一个 30 个字符的字符串并将其分成长度为 7、5、5、7 和 6 的子字符串,以便稍后操作。这是我开始尝试的:
void breakString(const char *lineStr) {
char a[7] = " "; //I tried with them all initialized empty and without doing so.
char b[5]; //Didn't seem to make a difference.
char c[5];
char d[7];
char e[6];
//sscanf(lineStr, "%7s", &a); //tried sscanf at first, but didn't know how to
strncpy(a, lineStr, 7); //scan the middle so i switched to strncpy
strncpy(b, lineStr + 7, 5);
//continue this pattern for c,d,e
(rest of function here, where each substring is manipulated accordingly.)
我通过打印子字符串a
和b
(以及将它们strcmp()
到正确的输出(来测试第一位,但它并不完全有效。我不断变得格外胡言乱语。例如,如果传递的完整字符串是 "abcdefghijklmnopqrstuvwxyz1234"
,则a
应"abcdefg"
、 b
应"hijkl"
,依此类推。但是,当我打印a
时,它显示为"abcdefg^#@%^&"
,每个子字符串后面都有一些随机分类的字符。
我做错了什么?还是有更好的方法来以不同的方式实现这一点?
我总是得到额外的胡言乱语...
这是因为如果源长度超过传递的大小,strncpy()
不会在目标末尾隐式追加 null 字符。在 C 语言中,字符串是以 null 结尾的字符数组。
因此,在此之后:
strncpy(a, lineStr, 7);
如果源长度超过传递的大小,则需要在末尾添加空字符,如下所示:
a[7] = ' ';
缓冲区大小应为 +1,以容纳缓冲区末尾的空字符:
char a[8];
char b[6];
char c[6];
char d[8];
char e[7];
您应该尽量避免使用strncpy()
因为您需要手动处理附加空字符。相反,请使用保证始终以 null 终止目标的东西,例如 snprintf()
。你可以做:
char a[8];
snprintf(a, 8, "%s", lineStr);
您无需附加终止空字符,它会自动附加到写入的内容之后。在此处阅读有关snprintf()
的更多信息。
附加:
您尝试初始化空数组的方式不正确:
char a[7] = " ";
这不是空数组,但这实际上将使用空格字符初始化数组(a[0])
的第一个元素,其余元素将使用 0
初始化。要初始化空数组,您可以执行以下操作:
char a[8] = {0};
这将使用 0
初始化数组的所有元素。
你的问题可以用strncpy
来解决,但你永远不应该使用这个函数,因为它的精确语义被广泛误解并且非常容易出错。
有关信息,请阅读 https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/。
此外,应使数组比计划为空终止符存储到其中的字符数长一个字节。
这是适合您情况的简单解决方案:
#include <stdio.h>
void breakString(const char *lineStr) {
char a[7+1] = ""; /* destination strings must be initialized */
char b[5+1] = ""; /* because the %c conversion specifier */
char c[5+1] = ""; /* will set a null terminator. */
char d[7+1] = "";
char e[6+1] = "";
if (strlen(lineStr) >= 7+5+5+7+6 &&
sscanf(lineStr, "%7c%5c%5c%7c%6c", a, b, c, d, e) == 5) {
/* string was long enough, fields correctly initialized */
printf("a: %snb: %snc: %snd: %sne: %sn", a, b, c, d, e);
}
}
int main() {
breakString("abcdefghijklmnopqrstuvwxyz0123456789");
return 0;
}
输出:
a: abcdefg
b: hijkl
c: mnopq
d: rstuvwx
e: yz0123
虽然此解决方案简单简洁,但我建议您采用不同的方法,使用实用程序函数。事实上,sscanf
解决方案使用了一组非常不寻常的转换说明符,这将使大多数程序员扬眉吐气并拒绝它。此外,它不适合将可变数量的字符提取到适当大小的子数组中。
这是一种不同的方法:
#include <stdio.h>
size_t getchunk(char *dest, size_t n, const char *str) {
size_t i;
for (i = 0; i < n && *str; i++) {
dest[i] = *str++;
}
dest[i] = ' ';
return i;
}
void breakString(const char *lineStr) {
char a[7+1];
char b[5+1];
char c[5+1];
char d[7+1];
char e[6+1];
size_t pos = 0;
pos += getchunk(a, 7, lineStr + pos);
pos += getchunk(b, 5, lineStr + pos);
pos += getchunk(c, 5, lineStr + pos);
pos += getchunk(d, 7, lineStr + pos);
pos += getchunk(e, 6, lineStr + pos);
if (e[0] != ' ') {
/* string was long enough, fields correctly initialized */
printf("a: %snb: %snc: %snd: %sne: %sn", a, b, c, d, e);
}
}
int main() {
breakString("abcdefghijklmnopqrstuvwxyz0123456789");
return 0;
}
sscanf((
有了sscanf()
,你可以做到
sscanf(lineStr, "%7c%5c%5c%7c%6c", a, b, c, d, e);
a[7]=b[5]=c[5]=d[7]=e[6]=' ';
%c
可用于读取超过 1 个字节。 %7c
最多读取 7 个字节。但