【问题标题】:How to check if a string exists in an array of character arrays如何检查字符串是否存在于字符数组数组中
【发布时间】:2015-04-06 01:13:18
【问题描述】:

我正在寻找一种方法来检查特定字符串是否存在于大型字符串数组中。该数组是多维的:all_strings[strings][chars];。所以本质上,这个数组是一个字符数组。每个字符数组以 '\0' 结尾

给定另一个字符数组,我需要检查这些字符是否已经在 all_strings 中,有点类似于 python 的 in 关键字。

我完全不知道该怎么做,我知道 strcmp 可能会有所帮助,但我不确定如何实现它。

【问题讨论】:

  • 只需逐个字符串循环遍历您的all_strings 列表,然后针对您要比较的字符串(其他字符数组)执行strcmp。

标签: c arrays string


【解决方案1】:

正如 lurker 所建议的,天真的方法是简单地循环调用 strcmp 的字符串数组。不幸的是,他的string_in 功能由于对sizeof(string_list) 的误解而被破坏,并且应该看起来像这样:

#include <string.h>
int string_in(char *needle, char **haystack, size_t haystack_size) {
    for (size_t x = 0; x < haystack_size; x++) {
         if (strcmp(needle, haystack[x]) == 0) {
             return 1;
         }
    }
    return 0;
}

然而,这是相当低效的。如果您只是偶尔使用一次,特别是在少量字符串上,它会起作用,但是如果您正在寻找一种有效的方法来一次又一次地执行搜索,请更改每个搜索查询搜索,我会考虑的两个选项是:

  • 如果all_strings是相对静态的,你可以像这样对你的数组进行排序:qsort(all_strings, strings, chars, strcmp);... 然后当你想确定一个单词是否存在时,你可以使用bsearch执行一个binary search like所以:char *result = bsearch(search_query, all_strings, strings, chars, strcmp);。请注意,当all_strings 发生变化时,您需要再次对其进行排序。
  • 如果all_strings 更改过于频繁,您可能会受益于使用其他一些数据结构,例如trie 或hash table。

【讨论】:

    【解决方案2】:

    使用for 循环。 C 没有像 Python 的 in 这样的内置函数:

    int i;
    
    for ( i = 0; i < NUM_STRINGS; i++ )
        if ( strcmp(all_strings[i], my_other_string) == 0 )
            break;
    
    // Here, i is the index of the matched string in all_strings.
    //   If i == NUM_STRINGS, then the string wasn't found
    

    如果你想让它表现得像 Python 的 in,你可以把它变成一个函数:

    // Assumes C99
    #include <string.h>
    #include <stdbool.h>
    
    bool string_in(char *my_str, char *string_list[], size_t num_strings)
    {
        for ( int i = 0; i < num_strings; i++ )
            if (strcmp(my_str, string_list[i]) == 0 )
                return true;
    
        return false;
    }
    

    【讨论】:

    • 其实C++11有范围循环,类似于Python的in。
    • @user4098326 确实如此。但问题仅限于 C.
    • 您的string_in 函数错误...考虑sizeof string_list 的值。
    • @undefinedbehaviour 感谢您指出这一点。这就是我在感冒和发烧时回答 SO 问题的结果。我更正了它,因为我无法删除已接受的答案。
    【解决方案3】:

    您可以简单地检查字符串数组中是否存在字符串。更好的解决方案可能是实际返回字符串:

    /*
     * haystack: The array of strings to search.
     * needle: The string to find.
     * max: The number of strings to search in "haystack".
     */
    char *
    string_find(char **haystack, char *needle, size_t max)
    {
        char **end = haystack + max;
        for (; haystack != end; ++haystack)
            if (strcmp(*haystack, needle) == 0)
                return *haystack;
        return NULL;
    }
    

    如果您想要一个集合的行为,其中数组中的所有字符串都是唯一的,那么您可以这样使用它:

    typedef struct set_strings {
        char **s_arr;
        size_t count;
        size_t max;
    } StringSet;
    .
    .
    .
    int
    StringSet_add(StringSet *set, const char *str)
    {
        // If string exists already, the add operation is "successful".
        if (string_find(set->s_arr, str, set->count))
            return 1;
    
        // Add string to set and return success if possible.
        /*
         * Code to add string to StringSet would go here.
         */
        return 1;
    }
    

    如果你想对字符串做一些实际的事情,你也可以这样使用它:

    /*
     * Reverse the characters of a string.
     *
     * str: The string to reverse.
     * n: The number of characters to reverse.
     */
    void
    reverse_str(char *str, size_t n)
    {
        char c;
        char *end;
    
        for (end = str + n; str < --end; ++str) {
            c = *str;
            *str = *end;
            *end = c;
        }
    }
    .
    .
    .
        char *found = string_find(words, word, word_count);
        if (found)
            reverse_str(found, strlen(found));
    

    作为一种通用算法,这是相当有用的,甚至可以根据需要应用于其他数据类型(当然需要进行一些返工)。正如undefined behaviour's answer 所指出的,它在大量字符串上不会很快,但对于简单和小型的东西来说已经足够了。

    如果您需要更快的东西,该答案中给出的建议很好。如果您自己编写代码,并且能够保持排序,那么这样做是个好主意。这允许您使用比线性搜索更好的搜索算法。标准bsearch 很棒,但是如果您想要适合快速插入的东西,您可能需要一个搜索例程,为您提供插入新项目的位置,以避免在bsearch 返回@ 之后搜索位置987654327@。换句话说,既然你可以搜索一次并完成同样的事情,为什么还要搜索两次呢?

    【讨论】:

    • 我喜欢你调整了我的代码以从表中返回字符串。这是朝着从集合中删除字符串的方向迈出的良好一步。我也喜欢您介绍的界面,它使更改StringSet_add 以使用其他集合变得简单。您是否经常开发关联数组数据结构和算法?如果你有,我会关注你的 github 个人资料。
    • @undefinedbehaviour 老实说,我没有 github 个人资料。我也不常用开发这种结构和相关算法,但我发现当我这样做时,我的代码中的一个常见模式与我上面发布的类似。
    • 我发现当需要快速插入(或删除,或任何其他变体)时,其他数据结构是合适的。我倾向于偏向 PATRICIA,因为正如您提到的,您可以返回要插入的位置(尽管插入的过程需要更多信息,因此比仅仅 在那里插入 更复杂)。不过,我很好奇……reverse_str 是怎么回事?如果遇到回文怎么办?
    • @undefinedbehaviour 关于你的好奇心,我不确定你指的是什么,除了我使用str != --end 而不是str &lt; --end 这可能会导致可怕的事情指定要交换的偶数个字节。不过现在已经解决了。如果您要问效率(为什么我不在循环中添加 if (*str == *end) 条件?),那是因为无条件交换在我的机器上更快,并且可能在其他机器上也更快(与分支相比,分支“慢”其他一些指令,尤其是数据移动指令)。
    猜你喜欢
    • 2020-07-06
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 2021-12-27
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多