Как я могу перебирать строку, а также знать индекс (текущая позиция)?

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

Итак, что лучше всего использовать в С++ для итерации через перечислимый объект, а также отслеживать текущий индекс?

Ответ 1

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

Ответ 2

Вот так:


    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;

Ответ 3

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

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

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

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

Ответ 4

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

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. Что бы ни было проще и чище для вас.

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

Ответ 5

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

Ответ 6

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

const char* strdata = str.c_str();

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

Ответ 7

Так как std::distance - это только постоянное время для итераторов с произвольным доступом, я бы предпочел явно арифметику итератора. Кроме того, поскольку мы пишем здесь код С++, я верю, что более идиоматическое решение на С++ предпочтительнее, чем подход C-стиля.

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

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