我正在尝试制作字符串解析器,但出了点问题

  • 本文关键字:问题 字符串 c
  • 更新时间 :
  • 英文 :


我试图制作一个文本解析器,它根据空格字符分隔字符串中的单词。然而,有些地方出了问题。

#include <stdio.h>
#include <string.h>
int main() {
//the string should end with a space to count the all words
char name[30] = "hello world from jordan ";
int start = 0;
int end = strlen(name);
int end_word = start;
char full[20][20];
memset(full, 0, 400);
int number_of_words = 0;
for (int w = 0; w < end; w++) {
if (name[w] == ' ') {
number_of_words++;
}
}
int counter = 0;
while (counter < number_of_words) {
for (int i = start; i < end; i++) {
if (name[i] == ' ') {
start = i;
break;
}
}
for (int j = end_word; j < start; j++) {
full[counter][j] = name[j];
}
end_word = start;
start++;
counter++;
}
for (int x = 0; x < 20; x++) {
for (int y = 0; y < 20; y++) {
printf("%c", full[x][y]);
}
printf("%d", x);
}
return 0;
}

当我运行代码时,发生了一件奇怪的事情:

hello0 world1 from2 jor3dan45678910111213141516171819

前三个字是以正确的方式初始化的,但第四个字不是,我不知道为什么会发生这种情况。

我想要对这个问题的解释,如果可能的话,我想要一种更有效的方法来写这个代码,而不需要指针指针

注意:我是一个初学者,这就是为什么我要求一个没有指针的解决方案

首先,试图避免C中的指针将是(非常(困难的。从本质上讲,数组在你想用它们做任何有用的事情的那一刻就变成了指针。数组订阅是语法上优于指针的算术运算(foo[2]*(foo + 2)相同(。将数组传递给函数将使其衰减到指向第一个元素的指针。

无论你是否意识到,你都会在代码中多次使用指针。


至于代码。。。

快速提示:在处理内存大小/索引时,size_t而不是int是合适的类型。我将在";校正的";版本的代码,您应该尝试在一般情况下使用它,继续前进

输出有点令人困惑,因为所有内容都打印在一行上。让我们清理一下,并添加一些调试信息,比如存储的每个字符串的长度。

for (size_t x = 0; x < 20; x++) {
printf("%zu [length: %zu]: ", x, strlen(full[x]));
for (size_t y = 0; y < 20; y++)
printf("%c", full[x][y]);
putchar('n');
}

现在,我们得到了以下几行的输出(为了简洁起见,有些重复项被折叠(:

0 [length: 5]: hello
1 [length: 0]:  world
2 [length: 0]:  from
3 [length: 0]:  jor
4 [length: 3]: dan
5 [length: 0]: 
...
19 [length: 0]: 

从中我们可以看到一些值得注意的事情。

  • 我们还有一个额外的,第五个";字符串";,当我们只期待四个人的时候
  • 我们的第一个和第五个";字符串";具有明显正确的长度,而
  • 我们的第二个到第四个";字符串";具有0的表观长度并且似乎包括空格

零长度意味着我们的一些数组以零终止字节(''(开始,并且我们只看到输出,因为我们手动遍历了每个数组的全部。

请注意,大多数终端将执行";什么都没有";当要打印空字符时,这意味着我们似乎直接跳到我们的";字符串";。我们可以通过总是打印一些东西来更好地可视化正在发生的事情:

printf("%c", full[x][y] ? full[x][y] : '*');

在这种情况下,当我们遇到空字符时,我们会打印一个星号,给出输出:

0 [length: 5]: hello***************
1 [length: 0]: ***** world*********
2 [length: 0]: *********** from****
3 [length: 0]: **************** jor
4 [length: 3]: dan*****************
5 [length: 0]: ********************
...
19 [length: 0]: ********************

这非常清楚地表明了我们的角色在记忆中的位置。

主要问题是在这个循环中

for (int j = end_word; j < start; j++) {
full[counter][j] = name[j];
}

j被初始化到相对于name的开始的位置,但是被用于索引full的存储器偏移。排除我们的第一个子串,当end_word0时,这会使我们离每个子数组的第零个索引越来越远,最终跨越数组之间的边界。

这是因为C中的2D阵列在内存中连续排列。

为了解决这个问题,我们必须使用一个单独的索引来复制我们的字符,该索引从每个子数组的零开始。

for (size_t j = end_word, k = 0; j < start; j++, k++) {
full[counter][k] = name[j];
}

现在,当我们打印出数组时,我们可以将自己限制在已知的number_of_words(for (size_t x = 0; x < number_of_words; x++)(,从而得到输出:

0 [length: 5]: hello***************
1 [length: 6]:  world**************
2 [length: 5]:  from***************
3 [length: 7]:  jordan*************

这看起来大致正确,但包括";单词";。我们可以跳过这些空格,将end_word设置为下一个字符

start++;
end_word = start;
counter++;

现在我们的输出看起来是正确的分割:

0 [length: 5]: hello***************
1 [length: 5]: world***************
2 [length: 4]: from****************
3 [length: 6]: jordan**************

请注意,这些是(现在格式正确(以null结尾的字符串,可以使用%s说明符打印,如:

for (size_t x = 0; x < number_of_words; x++)  
printf("%zu [length: %zu]: %sn", x, strlen(full[x]), full[x]);

总的来说,这有点脆弱,因为它需要尾部定界空间才能工作,并且每次重复定界空间时(或者如果源字符串以空格开头(都会创建一个空字符串。


顺便说一句,这个类似的例子应该展示一种直接的方法来标记字符串,同时跳过所有分隔符,并包括一些重要的注释。

#include <stdio.h>
#include <string.h>
int main(void) {
char name[30] = "hello world from jordan";
char copies[20][30] = { 0 };
size_t length_of_copies = 0;
size_t hold_position = 0;
size_t substring_span = 0;
size_t i = 0;
do {
/* our substring delimiters */
if (name[i] == ' ' || name[i] == '') {
/* only copy non-zero spans of non-delimiters */
if (substring_span) {
/* `strncpy` will not insert a null terminating character
* into the destination if it is not found within the span
* of characters of the source string...
*/
strncpy(
copies[length_of_copies],
name + hold_position,
substring_span
);
/* ...so we must manually insert a null terminating character
* (or otherwise rely on our memory being initialized to all-zeroes)
* */
copies[length_of_copies++][substring_span] = '';
substring_span = 0;
}
/* let's assume our next position will be the start of a substring */
hold_position = i + 1;
} else
substring_span++;
/* checking our character at the end of the loop,
* and incrementing after the fact,
* let's us include the null terminating character as a delimiter,
* as we will only fail to enter the loop after processing it
*/
} while (name[i++] != '');
for (size_t i = 0; i < length_of_copies; i++)
printf("%zu: [%s]n", i + 1, copies[i]);
}

最新更新