在main中,我将一个字符串传递给另一个应该分隔字符串的函数,然后处理每个子字符串。在这种情况下,我需要取一个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^#@%^&"
,每个子字符串后面会有一些随机的字符组合。
我做错了什么?或者有更好的方法来实现这一点吗?
答案 0 :(得分:0)
使用sscanf()
,您可以
sscanf(lineStr, "%7c%5c%5c%7c%6c", a, b, c, d, e);
a[7]=b[5]=c[5]=d[7]=e[6]='\0';
%c
可用于读取超过1个字节。 %7c
最多可读取7个字节。但\0
不会自动添加。
感谢此方法的chqrlie。
或只是
sscanf(lineStr, "%7s%5s%5s%7s%6s", a, b, c, d, e);
如果lineStr
中没有空格。
或者
sscanf(lineStr, "%7[^\n]%5[^\n]%5[^\n]%7[^\n]%6[^\n]", a, b, c, d, e);
如果lineStr
没有\n
个字符。
其中格式字符串中的数字表示要复制的子字符串的宽度。
通过这种方式,您无需\0
手动终止字符串。 sscanf()
会处理它。
如果你必须使用`strncpy(),那么你就是在正确的轨道上。你可以做到
void breakString(const char *lineStr) {
char a[8];
char b[6];
char c[6];
char d[8];
char e[7];
strncpy(a, lineStr, 7);
a[7]='\0';
lineStr+=7;
strncpy(b, lineStr, 5);
b[5]='\0';
lineStr+=5;
strncpy(c, lineStr, 5);
c[5]='\0';
lineStr+=5;
strncpy(d, lineStr, 7);
d[7]='\0';
lineStr+=7;
strncpy(e, lineStr, 6);
e[6]='\0';
//lineStr+=6;
}
请注意,需要额外的一个字节来存储字符串的\0
字符。因此,阵列的大小也会相应改变。
答案 1 :(得分:0)
我不断得到额外的胡言乱语......
这是因为如果源的长度大于传递的大小,strncpy()
不会在目标末尾隐式附加空字符。 C语言中的字符串是以空字符结尾的字符数组。
因此,在此之后:
strncpy(a, lineStr, 7);
如果源的长度超过传递的大小,则需要在末尾添加空字符,如下所示:
a[7] = '\0';
缓冲区大小应为+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()
here。
其他:
尝试初始化空数组的方式不正确:
char a[7] = " ";
这不是空数组,但实际上这将使用空格字符初始化数组(a[0])
的第一个元素,其余元素将使用0
进行初始化。要初始化空数组,您可以执行以下操作:
char a[8] = {0};
这将使用0
初始化数组的所有元素。
答案 2 :(得分:0)
您的问题可以通过strncpy
来解决,但是您永远不应该使用此函数,因为它的精确语义被广泛误解并且极易出错。
阅读https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/了解相关信息。
此外,对于null终止符,您应该使数组的长度比计划存储到其中的字符数长一个字节。
以下是针对您案例的简单解决方案:
#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: %s\nb: %s\nc: %s\nd: %s\ne: %s\n", 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] = '\0';
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] != '\0') {
/* string was long enough, fields correctly initialized */
printf("a: %s\nb: %s\nc: %s\nd: %s\ne: %s\n", a, b, c, d, e);
}
}
int main() {
breakString("abcdefghijklmnopqrstuvwxyz0123456789");
return 0;
}