簡體   English   中英

我正在嘗試制作字符串解析器,但出了點問題

[英]I'm trying to make string parser but something is going wrong

我試圖制作一個文本解析器,它根據空格字符分隔字符串中的單詞。 但是,出了點問題。

#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 中的指針將(非常)困難。 就其性質而言,arrays 在您想對它們做任何有用的事情時立即成為指針。 數組訂閱是指針算法的語法糖( foo[2]*(foo + 2)相同)。 將數組傳遞給 function 將導致它衰減到指向第一個元素的指針。

無論您是否意識到,您都會在代碼中多次使用指針。


至於代碼...

快速說明: size_t ,而不是int ,是使用 memory 大小/索引時使用的適當類型。 我將在代碼的“更正”版本中使用它,您應該嘗試在一般情況下使用它,繼續前進。

output 有點令人困惑,因為所有內容都打印在一行上。 讓我們清理一下,並添加一些調試信息,例如您存儲的每個字符串的長度。

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');
}

現在我們得到 output,跨越幾行(為簡潔起見,一些重復折疊),如下:

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 ,並且似乎包含空格。

零長度意味着我們的一些arrays以空終止字節( '\0' )開頭,我們只看到 output 因為我們手動遍歷每個數組的整體。

請注意,當要打印 null 字符時,大多數終端將“什么都不做”,這意味着我們似乎直接跳到了我們的“字符串”。 我們可以通過總是打印一些東西來更好地可視化正在發生的事情:

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

在這種情況下,當我們遇到 null 字符時,我們會打印一個星號,從而得到 output:

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]: ********************

這非常清楚地顯示了我們的角色在 memory 中的位置。

主要問題是在這個循環中

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

j被初始化為相對於name開頭的 position ,但用於索引full的 memory 偏移量。 排除我們的第一個 substring,當end_word0時,這使我們離每個子數組的第零個索引越來越遠,最終跨越 arrays 之間的邊界。

這恰好起作用,因為 C 中的 2D arrays 在 memory 中連續布局。

為了解決這個問題,我們必須使用一個單獨的索引來復制我們的字符,每個子數組從零開始。

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

現在,當我們打印 arrays 時,我們可以將自己限制在已知的number_of_wordsfor (size_t x = 0; x < number_of_words; x++) ),給我們 output:

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

這看起來大致正確,但在“單詞”中包含了前面的空格。 我們可以通過將end_word設置為下一個字符來跳過這些空格:

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

現在我們的 output 看起來正確拆分:

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

請注意,這些是(現在已正確格式化)以空字符結尾的字符串,並且可以使用%s說明符打印,如下所示:

for (size_t x = 0; x < number_of_words; x++)  
    printf("%zu [length: %zu]: %s\n", 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] == '\0') {
            /* 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] = '\0';
                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++] != '\0');

    for (size_t i = 0; i < length_of_copies; i++)
        printf("%zu: [%s]\n", i + 1, copies[i]);
}

暫無
暫無

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

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