![](/img/trans.png)
[英]Splitting a string at a delimiter and storing substrings in char **array in 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);
為什么a
和b
輸入int
而不是size_t
?
size_t indexOf(const char *s, const char c, size_t start);
如果找不到從索引start
的字符串c
,這個函數應該返回什么? 讓我們假設應該返回字符串末尾的偏移量,因為實現slice
更方便。
使用這些約定, indexOf
和charCount
可以寫成:
#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.