web-dev-qa-db-fra.com

Comment puis-je parcourir une chaîne et connaître l'index (position actuelle)?

Souvent, lors de l'itération d'une chaîne (ou de tout objet énumérable), nous ne sommes pas seulement intéressés par la valeur actuelle, mais également par la position (index). Pour ce faire, utilisez string::iterator nous devons maintenir un index séparé:

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

Le style présenté ci-dessus ne semble pas supérieur au "style c":

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

En Ruby, nous pouvons obtenir le contenu et l'index de manière élégante:

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

Quelle est donc la meilleure pratique en C++ pour parcourir un objet énumérable et également garder une trace de l'index actuel?

54
pierrotlefou

Je n'ai jamais entendu parler d'une meilleure pratique pour cette question spécifique. Cependant, une bonne pratique en général consiste à utiliser la solution la plus simple permettant de résoudre le problème. Dans ce cas, l'accès sous forme de tableau (ou style c si vous voulez l'appeler ainsi) est le moyen le plus simple d'effectuer une itération tout en ayant la valeur d'index disponible. Donc, je recommanderais certainement cette façon.

46
TheUndeadFish

Comme ça:


    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;
48
Leandro T. C. Melo

Vous pouvez utiliser la distance de fonction STL standard mentionnée précédemment

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

En outre, vous pouvez accéder à string et à d'autres conteneurs avec l'interface de type c:

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

Une bonne pratique serait basée sur la lisibilité, par exemple:

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

Ou:

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

Ou votre original:

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

Etc. Tout ce qui est le plus facile et le plus propre pour vous.

Il n’est pas clair qu’il existe une pratique exemplaire dans la mesure où vous aurez besoin d’une variable de compteur quelque part. La question semble être de savoir si le lieu où vous le définissez et comment il est incrémenté fonctionne bien pour vous.

10
Alex Reynolds

Je voudrais utiliser it-str.begin () Dans ce cas particulier std :: distance et operator- sont les mêmes. Mais si container change en quelque chose sans accès aléatoire, std :: distance incrémente le premier argument jusqu'à atteindre le second, donnant ainsi un temps linéaire et l'opérateur ne compilera pas. Personnellement, je préfère le deuxième comportement - il est préférable d’être averti lorsque vous utilisez un algorithme de O(n) devenu O (n ^ 2) ...

3
maxim1000

Puisque std::distance n'est qu'un temps constant pour les itérateurs à accès aléatoire, je préférerais probablement l'arithmétique explicite des itérateurs. De plus, comme nous écrivons du code C++ ici, je pense qu'une solution plus idiomatique C++ est préférable à une approche de type C.

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

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

Pour les chaînes, vous pouvez utiliser string.c_str() qui vous renverra un const char *, qui peut être traité comme un tableau, exemple:

const char* strdata = str.c_str();

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