Hoe kan ik een string doorlopen en ook de index kennen (huidige positie)?

Vaak zijn we bij het doorlopen van een string (of een willekeurig object) niet alleen geïnteresseerd in de huidige waarde, maar ook in de positie (index). Om dit te bereiken door string::iteratorte gebruiken, moeten we een aparte index onderhouden:

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

De hierboven getoonde stijl lijkt niet superieur aan de ‘c-style’:

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

In Ruby kunnen we zowel inhoud als index op een elegante manier verkrijgen:

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

Dus, wat is de beste werkwijze in C++ om een opsombaar object te doorlopen en ook de huidige index bij te houden?


Antwoord 1, autoriteit 100%

Ik heb nog nooit gehoord van een best practice voor deze specifieke vraag. Een algemene best practice is echter om de eenvoudigste oplossing te gebruiken die het probleem oplost. In dit geval is toegang in array-stijl (of c-stijl als je het zo wilt noemen) de eenvoudigste manier om te herhalen terwijl de indexwaarde beschikbaar is. Dus ik zou die manier zeker aanraden.


Antwoord 2, autoriteit 90%

Zoals dit:


    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;

Antwoord 3, autoriteit 51%

U kunt de standaard STL-functieafstand gebruiken zoals eerder vermeld

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

Je hebt ook toegang tot strings en enkele andere containers met de c-achtige interface:

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

Antwoord 4, autoriteit 26%

Een goede praktijk zou gebaseerd zijn op leesbaarheid, bijvoorbeeld:

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

Of:

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

Of uw origineel:

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

Enz. Wat voor jou het gemakkelijkst en het schoonst is.

Het is niet duidelijk dat er één best practice is, omdat je ergens een tellervariabele nodig hebt. De vraag lijkt te zijn of waar je het definieert en hoe het wordt verhoogd, goed voor je werkt.


Antwoord 5, autoriteit 6%

Voor strings kun je string.c_str()gebruiken, wat je een const char* zal opleveren, die als een array kan worden behandeld, bijvoorbeeld:

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

Antwoord 6, autoriteit 6%

Ik zou it-str.begin() gebruiken
In dit specifieke geval zijn std::distance en operator- hetzelfde. Maar als de container verandert in iets zonder willekeurige toegang, zal std::distance het eerste argument verhogen totdat het de tweede bereikt, waardoor lineaire tijd en operator- niet compileren.
Persoonlijk geef ik de voorkeur aan het tweede gedrag – het is beter om op de hoogte te worden gesteld wanneer je algoritme van O(n) O(n^2) wordt…


Antwoord 7, autoriteit 4%

Aangezien std::distancealleen constante tijd is voor willekeurige toegang-iterators, zou ik waarschijnlijk de voorkeur geven aan expliciete iterator-rekenkunde.
Omdat we hier C++-code aan het schrijven zijn, geloof ik ook dat een meer idiomatische C++-oplossing de voorkeur verdient boven een benadering in C-stijl.

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

Other episodes