Как я могу перебрать строку, а также узнать индекс (текущую позицию)? - PullRequest
54 голосов
/ 22 августа 2009

Часто при переборе строки (или любого перечисляемого объекта) нас интересует не только текущее значение, но и позиция (индекс). Для этого с помощью 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 ++ для итерации через перечисляемый объект, а также для отслеживания текущего индекса?

Ответы [ 7 ]

48 голосов
/ 22 августа 2009

Как это:


    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;
45 голосов
/ 22 августа 2009

Я никогда не слышал о наилучшей практике для этого конкретного вопроса. Тем не менее, одна из лучших практик в целом - это использовать простейшее решение, которое решает проблему. В этом случае доступ в стиле массива (или в стиле c, если вы хотите так его называть) - это самый простой способ итерации, когда доступно значение индекса. Поэтому я, безусловно, рекомендую этот способ.

21 голосов
/ 22 августа 2009

Вы можете использовать стандартное расстояние функции STL, как указано выше

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

Кроме того, вы можете получить доступ к строке и некоторым другим контейнерам с помощью c-like интерфейса:

for (i=0;i<string1.length();i++) string1[i];
10 голосов
/ 22 августа 2009

Хорошая практика будет основана на удобочитаемости, например ::1001*

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;

Etc. Что бы вам ни было легче и чище.

Не ясно, есть ли лучший метод, поскольку вам понадобится переменная счетчика где-нибудь. Похоже, вопрос в том, хорошо ли для вас определено, где вы его определяете и как оно увеличивается.

3 голосов
/ 22 августа 2009

Я бы использовал it-str.begin () В данном конкретном случае std :: distance и operator- одинаковы. Но если контейнер изменится на что-то без произвольного доступа, std :: distance будет увеличивать первый аргумент до тех пор, пока он не достигнет второго, давая, таким образом, линейное время и оператор- не скомпилируется. Лично я предпочитаю второе поведение - лучше получать уведомление, когда ваш алгоритм из O (n) стал O (n ^ 2) ...

1 голос
/ 01 августа 2017

Поскольку 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;
}
0 голосов
/ 22 августа 2009

Для строк вы можете использовать string.c_str(), который вернет вам const char *, который можно рассматривать как массив, например:

const char* strdata = str.c_str();

for (int i = 0; i < str.length(); ++i)
    cout << i << strdata[i];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...