如何迭代字符串并知道当前位置的索引?

62

通常当我们迭代一个字符串(或任何可枚举对象)时,我们不仅对当前值感兴趣,还对位置(索引)感兴趣。为了通过使用 string::iterator 实现这一点,我们必须维护一个单独的索引:

string str ("Test string");
string::iterator it;
int index = 0;
for ( it = str.begin() ; it < str.end(); it++ ,index++)
{
    cout << index << *it;
}

上面展示的样式似乎并不比“c-style”更好:

string str ("Test string");
for ( int i = 0 ; i < str.length(); i++)
{
    cout << i << str[i] ;
}

在 Ruby 中,我们可以以一种优雅的方式获取内容和索引:

"hello".split("").each_with_index {|c, i| puts "#{i} , #{c}" }

那么,在C++中遍历可枚举对象并同时跟踪当前索引的最佳实践是什么?


在第二个代码块中要小心!str.length() 的类型是 std::string::size_type,因此您需要使用 std::string::size_type 声明变量 i,否则编译器会给出不必要的警告。 - User123
7个回答

55

像这样:


    std::string s("Test string");
    std::string::iterator it = s.begin();

    //Use the iterator...
    ++it;
    //...

    std::cout << "index is: " << std::distance(s.begin(), it) << std::endl;

49

我从未听说过针对这个具体问题的最佳实践。然而,一般来说,最佳实践是使用解决问题最简单的方法。在这种情况下,数组式访问(或者如果你想这么称呼它的话,C语言风格)是迭代并同时拥有索引值的最简单方式。因此,我肯定会推荐使用这种方式。


5
一个例子会很好:P - jenkizenki
1
@NickHall 他指的是原帖中第二个代码块作为一个例子。 - Trevor Jex

24

你可以像之前提到的那样使用标准 STL 函数 distance

index = std::distance(s.begin(), it);

同时,你可以使用类C的接口访问字符串和其他一些容器:

for (i=0;i<string1.length();i++) string1[i];

3
顺便提一句,在我的实现中,索引的工作速度比迭代器快得多(5倍甚至更多)。 - Andrew

13

一种良好的实践方法是基于易读性,例如:

string str ("Test string");
for (int index = 0, auto it = str.begin(); it < str.end(); ++it)
   cout << index++ << *it;

或者:

string str ("Test string");
for (int index = 0, auto it = str.begin(); it < str.end(); ++it, ++index)
   cout << index << *it;

或者是您最初的代码:

string str ("Test string");
int index = 0;
for (auto it = str.begin() ; it < str.end(); ++it, ++index)
   cout << index << *it;

等等。最容易和最干净的方式就是...

并没有明确的最佳实践,因为你需要在某个地方使用计数器变量。问题似乎是关于在哪里定义它以及如何递增它是否适合你。


2
我的上一个编辑是正确的,@Alex Reynolds。请查看C ++for循环初始化体中声明两个不同类型的变量是不可能的。这个 - Zaid Khan

3

对于字符串,您可以使用string.c_str(),它将返回一个const char*,可以被视为数组,例如:

const char* strdata = str.c_str();

for (int i = 0; i < str.length(); ++i)
    cout << i << strdata[i];

这个问题和我的类似,所以我宁愿在这里问,你如何比较stardata[i]的值和“ ”?有没有办法将其转换为char类型? - Sidharth Ghoshal

3

在这种情况下,我会使用it-str.begin()。在这种特殊情况下,std::distance和operator-是相同的。但是,如果容器变成没有随机访问的容器,std::distance将增加第一个参数直到达到第二个参数,从而给出线性时间,而operator-将无法编译。

个人而言,我更喜欢第二种行为——当您的算法从O(n)变为O(n^2)时,最好得到通知...


2

由于std::distance仅对随机访问迭代器具有常数时间复杂度,因此我更喜欢使用显式的迭代器算术运算。此外,既然我们在编写C++代码,我相信更符合C++惯用法的解决方案比C语言风格的方法更可取。

string str{"Test string"};
auto begin = str.begin();

for (auto it = str.begin(), end = str.end(); it != end; ++it)
{
    cout << it - begin << *it;
}

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