在C++中将一个字符与列表中的字符进行比较

4
有没有一种方法可以将一个charchar列表中的每个元素进行比较?
char ch;
if(ch == 'a' || ch == 'b' || ch == 'c')

是否有方法只进行以下操作

if(ch is one of {a, b, c})

1
使用std::find或尝试维护一个字符串而不是字符列表。 - andre
3
如果你正在处理C风格的字符串,查阅strchr()函数。 - Crowman
2
strchr 确实比答案中使用的任何“现代 C++”方法都要简单得多。 - Ben Voigt
8个回答

12

当你可以直接这样做时,为什么要编写lambda表达式或使用临时字符串对象:

if (strchr("abc", ch))

如果"abc"首先被绑定到一个字符串上,std::string s = "abc"。在这里使用strchr是否不正确? - Mars
@Dochevsky:strchr(some_string.c_str(), ch) 可以工作,但除非你有一个字符串对象出于其他原因,否则这将是浪费。构造 std::string 对象相对昂贵。 - Ben Voigt

6

使用:std::any_of

在 C++11 中:

std::string str="abc";

if(std::any_of(str.cbegin(), str.cend(), 
    [ch](const char& x){return x==ch; } ))
{

}

或者使用函数对象:
struct comp
{
    comp(char x) :ch(x){}
    bool operator()(const char& x) const
    {
        return x == ch;
    }
    char ch;

};

然后,
if(std::any_of(str.cbegin(), str.cend(),comp(ch) ))
{

}

编辑:为了使用C++的<algorithm>,可以尝试使用std::any_of,但可能不够高效。


2
+1,我总是忘记它需要使用谓词而不是一个值(在我看来这很遗憾)。 - jrok
1
我更喜欢这种解决方案,而不是使用std::find的解决方案,因为它更清晰地传达了你想要做什么。 - Escualo
这个算法在实现中使用了循环吗? - Mars
2
@Dochevsky: 就效率而言,最好的是查找表,然后是使用最小连续范围进行比较(请参见Doug的答案),接着是问题中的代码和Vlad的代码,它们在运行时应该是等价的,然后是像我回答中所说的strchr,再次之后是字符数组中的std::findstd::any_of,最慢的则是在可抛弃的字符串对象中进行迭代。 - Ben Voigt
@BenVoigt 现在我感觉我不应该发帖了 :'( 我看到 C++ 就开始对 STL 的 "算法方式" 着迷。 - P0W
显示剩余5条评论

4
您可以使用std::find。假设chars是您的字符数组,您需要查找ch
if(std::find(std::begin(chars), std::end(chars), ch) != std::end(chars))

4
一种方法是搜索字符串,像这样:
string abc("abc");
if (abc.find(ch) != string::npos) {
    ...
}

是的,成员函数 find<algorithm> 中的通用版本更易于使用(和阅读)。它甚至可以与临时的 string 对象一起使用,例如 string("abc").find(ch) 或者使用用户定义字面量 "abc"str.find(ch)。但与 <string.h> 的方式相比仍然非常冗长。 - Ben Voigt
@BenVoigt 你说得对 - 我选择 std::string 主要是出于"意识形态原因",虽然 C 库的函数在 C++ 中也完全可以使用。 - Sergey Kalinichenko

4
(此回答仅适用于不想使用C++标准库结构的情况。)
在您的特定情况下,您应该能够执行以下操作:
 if(ch >= 'a' && ch <= 'c')

我还为此情况使用了 fall-through switch:

 switch(ch)
 {
     case 'a':
     case 'b':
     case 'c':
     case 'e':
         ...
         break;
 }

有些人不喜欢使用穿透switch/case语句,但我认为它比一个庞大的布尔逻辑更少出错,并且在使用数据结构时性能更好。编译器处理switch语句非常出色。


这就是我即将发布的内容 :) - Grijesh Chauhan
1
这取决于所使用的字符集 - 这些字符不需要是连续的(与十进制数字不同)。 - Crowman
@Paul 这适用于 ASCII 和 UTF8。UTF16 将是宽字符。 - Doug T.
2
@DougT.:同意,这样做会有问题。例如,对于EBCDIC编码,类似if(ch >= 'a' && ch <= 'm')的代码将失败。 - Crowman
我认为EBCDIC足够晦涩,可以假装它不存在。事实上,我认为假设('z' - 'a' == 25)比C++本身更具可移植性是一个安全的赌注。 - Benjamin Lindley
这可能是一个安全的赌注,但编写不假设它的代码甚至更安全,在这种特定情况下,实际上更容易。 - Crowman

1
如果您能够使用C++11引入的可变参数模板参数,那么您可以做出类似于这样的操作:
template <typename Key, typename Value>
inline bool in(const Key& key, const Value& value) {
    return key == value;
}

template <typename Key, typename Value0, typename ...ValueN>
inline bool in(const Key& key, const Value0& value, ValueN &&...args) {
    return (key == value ? true : in(key, std::forward<ValueN>(args)...));
}

我用它来处理像这样的字符串:


if (in(some_string, "base", "os", "io", "coroutine", "debug")) ...

但是支持比较的其他类型(如char)也应该可以正常工作。

希望能够帮到你。祝好运!


0

作为另一种选择,可以创建一个包含字符的set,并检查它是否在其中;

std::set<char> mySet = {'a','b','c'};  // C++11 initializer list

if(mySet.find('d') != mySet.end()) {
    ...
}

0

我有点惊讶,竟然没有人建议使用find_first_of。

    char c('e');

// we can check if c is undesirable
    const std::string unwanted("abc");
    bool undesirable = (unwanted.find_first_of(c) != std::string::npos);
 
// OR we can check if c is desirable
    const std::string wanted("def");
    bool desirable = (wanted.find_first_of(c) != std::string::npos); //..or check if it's desirable.

我使用这个(也许我不应该?)来忽略字符串迭代器中不需要的字符...

/** in and out are string iterators.
  * skip over any undesirable characters by matching 
  * against desirable and looking for npos.
 **/
const std::string ok("!+-./0123456789:^ABFIORmn");
while (ok.find_first_of(*in) == string::npos && in < out) {
    in++;
}

这样做的一个好处是,通过将更频繁出现的字符放在字符串的前面,可以节省一点时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接