关于c ++:使用for_each迭代NULL终止的字符串数组


Iterate over NULL terminated array of strings using for_each

可以使用for-each迭代以空结尾的字符串:

1
2
3
4
5
6
7
8
9
10
11
const char *name ="Bob";

void func(const char &arg)
{
   cout << arg;
}

int main()
{
    for_each(name, name + strlen(name), func);
}

对于以空结尾的字符串列表(不必首先确定列表的总长度),是否有类似的可能,例如:

1
const char *names[] = {"Bob","Adam","Simon", NULL };


std::对于一个范围内的每个"迭代",因此要将它与一个长度不确定的数组一起使用,需要使用自定义迭代器来表示数组的结尾(在空成员上)。如果您坚持使用以空结尾的char*数组,您当然可以为它创建自己的for_每个函数,例如:

1
2
3
4
5
6
7
8
9
10
11
template <typename Function>
void for_each_in_null_terminated_cstring_array(const char** array, Function f)
{
    while (*array) {
        f(*array);
        array++;
    }
}

const char *names[] = {"Bob","Adam","Simon", NULL };
for_each_in_null_terminated_cstring_array(names, func);

不过,我并不推荐这种解决方案。

编辑:是的,越一般越好,不是吗?

1
2
3
4
5
6
7
8
template <typename T, typename Function>
void for_each_in_null_terminated_array(T* array, Function f)
{
    while (*array) {
        f(*array);
        array++;
    }
}

(这里是我前面提到的一个以空结尾的("false")迭代器的实现——根据下面的建议进行一两次更改。应该是个真正的住院医生)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
template <class T>
class nt_iterator: public std::iterator<std::input_iterator_tag, T>
{
public:
    typedef typename nt_iterator<T>::pointer pointer;
    typedef typename nt_iterator<T>::value_type value_type;

    nt_iterator(): p(), pte(true) {}
    nt_iterator(pointer p_): p(p_), pte(!p_) {}
    nt_iterator(const nt_iterator<T>& rhs): p(rhs.p), pte(rhs.pte) {}
    nt_iterator<T>& operator++() {
        ++p;
        if (!*p) pte = true; // once past-the-end, always past-the-end
        return *this;
    }
    nt_iterator<T> operator++(int) {
        nt_iterator n(*this);
        operator++();
        return n;
    }
    bool operator==(const nt_iterator<T>& rhs) {
        return pte && rhs.pte || p == rhs.p;
    }
    bool operator!=(const nt_iterator<T>& rhs) {
        return !(operator==(rhs));
    }
    value_type operator*() { return *p; }

private:
    pointer p;
    bool pte; // past-the-end flag
};

以及它的使用方法:

1
2
3
4
5
6
7
8
9
void print(const char* str);

int main()
{
    const char* array[] = {"One","Two","Three", NULL,"Will you see this?"};
    std::for_each(nt_iterator<const char*>(array),
                  nt_iterator<const char*>(),
                  print);
}

它可能比循环版本慢一点,因为等价性检查的数量增加了——当然,与打印文本相比,速度差异是微不足道的——但是应该注意的是,std::for_each并不能神奇地使循环更快(事实上,您可能会惊讶地看到编译器供应商如何定义e函数-也就是说,如果你期望太多的话)。


扩大巴塞利夫的答案与充分工作的解决方案。

自定义迭代器的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class T>
class NullTerminatedIterator
    :public std::iterator<std::forward_iterator_tag,
    T,ptrdiff_t,const T*,const T&>
{
public:
    typedef NullTerminatedIterator<T> NTI;

    NullTerminatedIterator(T * start): current(start) {}
    NTI & operator++() {current++; return *this;}
    T & operator*() { return *current; }
    static NTI end() { return NTI(0); }
    bool operator==(const NTI & that) { return *current == *that.current; }
    bool operator!=(const NTI & that) { return *current != *that.current; }
private:
    T * current;
};

然后像这样使用:

1
2
3
4
5
const char *names[] = {"Bob","Adam","Simon", NULL};

NullTerminatedIterator<char*> iter((char**)names);

for_each(iter, NullTerminatedIterator<char*>::end(), func);

nullTerminateEditor的基类取自此自定义迭代器问题。

这只会在每次调用for_期间按要求遍历列表。


1
const char *names[] = {"Bob","Adam","Simon" };

你可以打电话

1
std::for_each(names, names + sizeof(names)/sizeof(names[0]), func );

或者,更好的是,使用两个助手函数:

1
std::for_each(begin(names), end(names), func );

当然,当数组衰减为指针时,这会失败(但至少编译器那时不会接受它)。如果您必须依赖尾随的NULL,您要么需要编写自己的循环函数,要么在手前计数,就像std::strlen()一样:

1
2
3
4
std::ptr_diff_t num = std::find( names
                               , names + std::numeric_limits<std::size_t>::max()
                               , NULL);
std::for_Each( names, names+num, func );


有很多答案告诉你你可以做什么。然而,对您的特定问题的回答只是"不,您不能":)


1
2
3
4
5
6
7
    // C version
    const char* vars[16]={"$USER","$HOME","$DISPLAY","$PASSWORD",0};

    for(const char** pc = vars; *pc!=0; pc++)
    {
            printf("%s",*pc);
    }

我知道这不是针对每个人的,但我想用旧的正则for循环来做同样的事情。这一个来自msdn博客:

This reinterpretation of a double-null-terminated string as really a list of strings with an empty string as the terminator makes writing code to walk through a double-null-terminated string quite straightforward:

1
2
3
for (LPTSTR pszz = pszzStart; *pszz; pszz += lstrlen(pszz) + 1) {
   // ... do something with pszz ...
}

我觉得有点干净!


1
2
3
4
5
6
7
8
9
10
11
template <class T>
struct NullTerminatedIterator {
  typedef NullTerminatedIterator<T> NTI;
  T * current;
  NTI & operator++() {current++; return this;}
  T & operator*() {return *current;}
  NullTerminatedIterator(T * start): current(start) {}
  static NTI end() {return  NTI(0);}
  bool operator==(const NTI & that) {return current==that.current;}

}


不能将传递给func的参数替换为指向const char的指针的引用,以实现所需的功能。就像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
const char *names[] = {"Bob","Adam","Simon" };

void func( const char* &arg )
{
   cout << arg << endl;
}

int main()
{
    for_each( names,
              names + sizeof( names ) / sizeof( names[ 0 ] ),
              func );
}

显然,对于以空结尾的字符串数组,只需从数组大小中减去1…


将它们添加到一个容器中,并使用for-each迭代它们我用一个向量作为例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void function(string name)
{
    cout << name;
}

int main()
{
    vector<string> nameVector;

    nameVector.push_back("Bob");
    nameVector.push_back("Adam");
    nameVector.push_back("Simon");

    for_each(nameVector.begin(), nameVector.end(), function);

    return 0;
}

可以对编译时大小的数组使用sizeof()。

1
2
3
4
5
6
const char *names[] = {"Bob","Adam","Simon" };
std::for_each(names, names + sizeof(names)/sizeof(*names), [](const char* arg) {
    std::cout << arg <<"
"
;
});
std::cin.get();

对于动态调整大小的数组,您应该使用std::vector并对其进行迭代。

请原谅我使用了lambda,您的编译器(可能)不支持它们。