如何在C中将字符串分成多个特定长度的其他字符串?

时间:2018-05-01 06:25:29

标签: c arrays string parsing strncpy

在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.)

我通过打印子串ab(以及strcmp()将它们打印到正确的输出来测试第一位),但它并不完全有效。我不断得到额外的胡言乱语。例如,如果传递的完整字符串为"abcdefghijklmnopqrstuvwxyz1234",则a应为"abcdefg"b应为"hijkl",依此类推。但是,当我打印a时,它会显示为"abcdefg^#@%^&",每个子字符串后面会有一些随机的字符组合。

我做错了什么?或者有更好的方法来实现这一点吗?

3 个答案:

答案 0 :(得分:0)

1)sscanf()

使用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()会处理它。

2)strncpy()

如果你必须使用`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;
}