Как разделить строку на несколько других строк определенной длины в C? - PullRequest
0 голосов
/ 01 мая 2018

В основном я передаю строку другой функции, которая должна разделять строку, а затем работаю с каждой подстрокой. В этом случае мне нужно взять строку из 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^#@%^&" с некоторым произвольным набором символов, следующих за каждой подстрокой.

Что я делаю не так? Или есть лучшие способы реализовать это по-другому?

Ответы [ 3 ]

0 голосов
/ 01 мая 2018

Я продолжаю получать лишнюю тарабарщину ...

Это потому, что 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(), потому что вам нужно вручную позаботиться о добавлении нулевого символа. Вместо этого используйте что-то, что всегда гарантирует нулевое завершение пункта назначения, например snprintf(). Вы можете сделать:

char a[8];
snprintf(a, 8, "%s", lineStr);

Вам не нужно добавлять завершающий нулевой символ, он автоматически добавляется после написанного содержимого. Подробнее о snprintf() здесь .


Дополнительно:

Неправильный способ инициализации пустого массива:

char a[7] = " "; 

Это не пустой массив, но он фактически инициализирует первый элемент массива (a[0]) пробелом, а остальные элементы будут инициализированы 0. Чтобы инициализировать пустой массив, вы можете сделать:

char a[8] = {0};

Это инициализирует все элементы массива с 0.

0 голосов
/ 01 мая 2018

Ваша проблема может быть решена с помощью 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: %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;
}
0 голосов
/ 01 мая 2018

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 для строк необходим дополнительный один байт. Таким образом, размеры массивов соответственно меняются.

...