簡體   English   中英

使用C將字符串拆分為子字符串

[英]Splitting a string into substrings using C

我正在嘗試制作一個基於特定字符拆分字符串的程序。

使用的數據結構:

typedef struct pieces {
    char **members;
    size_t len;
} pieces;

函數聲明:

pieces split     (const char *s, const char c);
size_t charCount (const char *s, const char c);
char *slice      (const char *s, int a, int b);
size_t indexOf   (const char *s, const char c, size_t start);

charCount -> 字符出現在字符串中的次數。

indexOf -> 返回字符串中給定字符第一次出現的索引,從索引start indexOf("Stack Overflow", 'O', 0) == indexOf("Stack Overflow", 'O', 3)

我已經實現了這樣的slice

char *slice  (const char *s, int a, int b)
{
    if (a > b || a == b)  
        return NULL;

    if (b > strlen(s))      // Only slice upto end if tried to slice out of index
        b = strlen(s);      

    size_t len = b - a + 1;
    char *slice = malloc(sizeof(char) * len);

    for (size_t i = a; i < b; i++)
        slice[i - a] = s[i];
    slice[len - 1] = '\0';

    return slice;
}

我對split功能感到困惑:

pieces split (const char *s, const char c)
{
    // Is this the right way to make room for incoming slices ?
    pieces arr;
    arr.len = charCount(s, c) + 1;
    arr.members = malloc(sizeof(char *) * arr.len);

    // Should I do something like this to insert slices ?
    for (size_t i = 0; i < strlen(s);)
    {
        int seperator_idx = indexOf(s, c, i);
        char *piece = slice(s, i, seperator_idx);

        arr.members[i] = piece;     // Should I use strdup ??

        i = seperator_idx + 1;
    }

    // What about the last slice ?

    return arr;
}

要將字符串拆分為 1 個字符,我會執行以下操作:

#include <string.h>
#include <stdlib.h>

int count_words(char const *str, char const delim)
{
    int count = 0;
    int i = 0;

    for (; str[i]; i++) {
        // the next character is the beggining of a new string
        if (str[i] == delim && str[i + 1] != delim)
            count++;
    }
    // for safety
    if (str[i - 1] != delim)
        count++;

    return count;
}

int word_length(char const *str, char const delim)
{
    int length = 0;

    // while we're on a valid character, increase the word length
    while (str[length] && str[length] != delim)
        length++;

    return length;
}

pieces split(char const *str, char const delim)
{
    // move the pointer until we're not on the delimiter
    while (*str == delim)
        str++;

    // prepare the string array
    pieces p;
    p.len = count_words(str, delim);
    p.members = malloc(p.len * sizeof(char *));

    // for each string
    for (int i = 0; i < p.len; i++) {

        // copy the string
        int length = word_length(str, delim);
        p.members[i] = strndup(str, length);

        // move the pointer until we're not on the delimiter
        str += length;
        while (*str == delim)
            str++;
    }
    return p;
}

提議的原型存在一些問題:

  • pieces split(const char *s, const char c); 目前尚不清楚c的連續出現是否表示空子字符串或單個分隔符(如strtok )。 讓我們假設應該接受空子字符串。 const限定c是矯枉過正的,在原型中並非毫無意義

  • size_t charCount(const char *s, const char c); 關於const char c的相同評論。 假設空終止符不是字符串的一部分,因此charCount("abc", '\0')為零。

  • char *slice(const char *s, int a, int b); 為什么ab輸入int而不是size_t

  • size_t indexOf(const char *s, const char c, size_t start); 如果找不到從索引start的字符串c ,這個函數應該返回什么? 讓我們假設應該返回字符串末尾的偏移量,因為實現slice更方便。

使用這些約定, indexOfcharCount可以寫成:

#include <stddef.h>

size_t indexOf(const char *s, const char c, size_t start) {
    while (s[start] && s[start] != c)
        start++;
    return start;
}

size_t charCount(const char *s, const char c) {
    size_t count = 0;
    while (*s) {
        count += (*s++ == c);
    }
    return count;
}

您的slice函數有多個問題:

  • 如果a == b ,它應該返回一個空字符串,
  • len命名為不是子字符串長度的東西是令人困惑的。 len定義為size_t len = b - a; 或使用size_t size = b - a + 1;
  • 如果a大於strlen(s)並且b > a ,則它具有未定義的行為。
  • 如果malloc()失敗,您應該優雅地返回NULL

這是修改后的版本:

#include <stdlib.h>

/* return an empty string if a >= b */
char *slice(const char *s, size_t a, size_t b) {
    size_t len = strlen(s);
    if (a > len)
        a = len;
    if (b < a)
        b = a;
    char *slice = malloc(b - a + 1);
    if (slice != NULL) {
        for (size_t i = a; i < b; i++)
             slice[i - a] = s[i];
        slice[b - a] = '\0';
    }
    return slice;
}

split函數也有問題:

  • 命名pieces結構arr令人困惑:它不是一個數組。
  • 您對arr.members的分配是正確的,但您應該測試是否分配成功。
  • 無需strdup()使用malloc()分配的slice的返回值。
  • 您應該為數組arr.members中的索引i和子字符串開頭的索引使用 2 個單獨的索引變量。
  • 循環應該寫一個測試所以split("", c)返回一個空字符串。
  • 如果indexOf在找不到c的情況下返回字符串的結尾,則最后一個切片不需要特殊情況。

這是修改后的版本:

pieces split(const char *s, const char c) {
    pieces arr;
    arr.len = charCount(s, c) + 1;
    arr.members = malloc(sizeof(*arr.members) * arr.len);
    if (arr.members != NULL) {
        for (size_t i = 0, start = 0; i < arr.len; i++) {
            size_t end = indexOf(s, c, start);
            arr.members[i] = slice(s, start, end);        
            start = end + 1;
            if (arr.members[i] == NULL) {
                /* free previous substrings and the members array */
                while (i-- > 0) {
                    free(arr.members[i]);
                }
                free(arr.members);
                arr.members = NULL;
                break;
            }
        }
    }
    return arr;
}

請注意這些最后的評論:

  • 如果在字符串中找不到字符時indexOf()返回(size_t)(-1) ,則上面編碼的split也有效。

  • slice()中重新計算字符串的長度是浪費的。 slice()應該假設參數值是正確的: 0 <= a < b <= strlen(s)

  • split沒有直接的方法返回錯誤。 members設置為NULL似乎是一個可行的解決方案。

  • 而不是slice() ,並假設indexOf返回字符串的有效偏移量,您可以使用 POSIX 標准函數strndup()

     arr.members[i] = strndup(s + start, end - start);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM