[C++] std::string.replace() maakt garbage?

Pagina: 1
Acties:

  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Ben er al zeker een uur mee bezig maar kom er eigenlijk niet uit. Het is punt is dat ik een replacefunctie hebt welke er als volgt uit ziet:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void lplhSymbol::setVariable(std::string name, std::string value) {
    std::string seq = "{" + name + "}";
    std::string::size_type pos = 0;
    
    while((pos = m_buffer.find(seq,pos)) != std::string::npos) {
        
        m_buffer.replace(pos,seq.length(),value);
        //m_buffer = m_buffer.substr(0,pos) + value + m_buffer.substr(pos + seq.length(),m_buffer.length() - pos + seq.length());
        
        pos++;
    }
    
    std::cout << "Uit VAL ==>" << m_buffer.c_str() << "<<==\n";
}
Regel 8 heb ik er voor de zekerheid even bijgezet maar die verschilt niet qua uitwerking in deze situatie. De waarde van m_buffer (std::string) is als volgt:
code:
1
<tr><td><a href="/policy?id={policyid}">{policyname}</a></td></tr>
Na de 'replace' ontstaat de volgende waarde:
code:
1
<tr><td><a href="/policy?id=0">{policyname}</a></td></tr>lt.css
Het gaat dan met name om de laatste 5 karakters (dit wisselt steeds). Dit lijkt mij een bug, wanneer de te replacen waarde kleiner is dan de originele dan kort hij de string niet in :? . Of zie ik nu iets helemaal over het hoofd.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


  • NMe
  • Registratie: Februari 2004
  • Laatst online: 20-11 11:59

NMe

Quia Ego Sic Dico.

Hmm, ik heb zelf niet genoeg verstand van C++ om te zien of je wat verkeerd doet, maar het ziet ernaar uit dat de string niet netjes afgesloten wordt door een \0 na de replace, waardoor je in het geheugen van andere variabelen/processen gaat zitten lezen. Of dat aan jou ligt weet ik niet, maar netjes ziet het er in elk geval niet uit. :X

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Verwijderd

De replace functie retourneert een nieuwe string reference. Dus volgens mij moet het zowieso dit zijn:

C++:
1
m_buffer = m_buffer.replace(pos,seq.length(),value);

Verder ben ik ook geen kei in C++ dus misschien zit er nog meer fout :?

  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Ook als ik een \0 handmatig toevoeg dan loopt hij keihard door en komt er steeds willekeurig garbage bij. Ik begin bijna aan de memorystate van mijn systeem te twijfelen, maar ook in de debugger treed deze fout op.

edit: Om het doorloopprobleem te verhelpen had ik dit bedacht:
C++:
7
8
9
        std::string tmp_buffer = m_buffer.substr(0,pos) + value + m_buffer.substr(pos + seq.length(),m_buffer.length() - pos - seq.length());
        //m_buffer.replace(pos,seq.length(),value);
        m_buffer = tmp_buffer;

[ Voor 40% gewijzigd door LauPro op 20-11-2006 08:54 ]

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


  • dsalden
  • Registratie: December 2005
  • Laatst online: 30-06 20:38
Ik zie ook niet zo direct een fout, trouwens als ik het hier test werkt het zonder problemen.
En bij mijn weten werkt de stl library niet met null-terminated strings, de functie c_str() die maakt van de stl string een null-terminated C-string.

A computer is almost human - except that it doesn't blame its mistakes on another computer


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
De eerste run ziet het er goed uit, maar als ik dan een paar keer opnieuw deze functie aan roep dan komt er garbage bij.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Zou het een mogelijkheid kunnen zijn dat een included (C) lib ervoor zorgt dat er zo nu en dan random geheugenblokken worden overschreven? Want ik zie dat er tamelijk wat memleaks in zitten, zal eens valgrind er overheen halen.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


Verwijderd

Ik heb het probleem geprobeerd te reproduceren maar met de volgende code
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <string>

std::string m_buffer("<tr><td><a href=\"/policy?id={policyid}\">{policyname}</a></td></tr>");

void test(std::string name, std::string value) { 
    name = name.insert(0, "{");
    name = name.insert(name.length(), "}");
    std::string::size_type pos = 0; 
     
    while((pos = m_buffer.find(name,pos)) != std::string::npos) { 
         
        m_buffer = m_buffer.replace(pos,name.length(),value); 
         
        pos++;
    } 
     
    std::cout << m_buffer.c_str() << "\n"; 
}

int main() {
    test("policyid", "0");
    test("policyname", "Policy 1");

    return 0;
}

krijg ik deze output (welke correct is afaik).
<tr><td><a href="/policy?id=0">{policyname}</a></td></tr>
<tr><td><a href="/policy?id=0">Policy 1</a></td></tr>

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
Kan niet 123 verklaren waarom het bij jouw voorbeeld fout gaat, maar volgens mij staat die pos++ er niet goed.

Als je nl aan het eind van de string iets vervangt met een lengte 0 wordt die pos groter dan de lengte van de string. Ligt aan find wat hij daarmee gaat doen dan.

[edit]
Iig ga je nu ook strings replacen in een gedeelte dat je net had gereplaced. Als dat je bedoeling is is dat natuurlijk ok, zo niet dan moet je de lengte van het gereplace-de gedeelte erbij op tellen.

[ Voor 27% gewijzigd door farlane op 20-11-2006 11:36 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Standalone werkt het ook prima. Ik krijg het idee dat de m_buffer al onjuiste informatie krijgt. Al hoewel dit tijdens het debuggen niet op te merken is (blijkbaar laat de debugger wel de waardes van de strings zien tot aan het 'einde', maar het programma loopt dan nog door). De pos++ zou wel moeten werken want als hij out of bounds is krijg je toch automatisch een npos, moet niet gekker worden dat find ook al zou doorlopen 8)7 .

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


Verwijderd

LauPro schreef op maandag 20 november 2006 @ 09:38:
Standalone werkt het ook prima.
[...]
Als het standalone prima werkt dan lijkt het me dat de fout dus ergens anders zit en niet in de functie die je hier dropt. Verder weten wij niets over die omgeving dus kunnen we je daar ook niet echt bij helpen op dit moment :).

  • TheNameless
  • Registratie: September 2001
  • Laatst online: 07-02 21:38

TheNameless

Jazzballet is vet!

Zijn er misschien andere componenten in je programma die ook gebruik maken van m_buffer?
Als m_buffer door meerdere threads gebruikt wordt kunnen dit soort rare problemen voor komen.

Ik heb het voorbeeldje hierboven van djingelz ook geprobeerd maar hier werkt het ook gewoon goed.

Ducati: making mechanics out of riders since 1946


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Het bleek aan het inlezen te liggen van de m_buffer. Deze komt namelijk uit een file:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    std::ifstream file(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
    
    if (file.is_open()) {
        ///@todo: is there a native C++ solution for this? I hate char* !!
        std::ifstream::pos_type size = file.tellg();
        
        char *buffer = new char[size];
        file.seekg(0,std::ios::beg);
        file.read(buffer,size);
        file.close();
        
        buffer[size] = 0;
        m_buffer = buffer;
        delete[] buffer;
    }
Door het ontbreken van regel 12 kwam er af en toe garbage in de string terecht. Is er trouwens ook een meer C++-native manier om zo'n std::string te vullen, dit was het beste wat ik kon vinden.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


Verwijderd

Even vlug uit het hoofd zou je volgens mij wel zoiets kunnen doen.
C++:
1
2
3
4
5
6
7
8
ifstream file(filename, ios::in | ios::binary | ios::ate);

if (file.is_open()) {
  while(file.good()) {
    m_buffer << (char)file.get();
  }
  file.close();
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21:56

.oisyn

Moderator Devschuur®

Demotivational Speaker

Kijk eens naar std::getline()
C++:
1
2
3
4
5
6
7
int main()
{
    std::string contents;
    std::ifstream file("in.txt");
    std::getline(file, contents, 0);
    std::cout << contents << std::endl;
}


Overigens hoef je c_str() niet aan te roepen om je string te outputten :)
Verwijderd schreef op maandag 20 november 2006 @ 08:52:
De replace functie retourneert een nieuwe string reference.
Onzin, hij retourneert zichzelf. Assignment is dus niet nodig.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • schoene
  • Registratie: Maart 2003
  • Laatst online: 21:33
Ik denk toch ook dat je beter niet pos++ doet, maar
pos += value.size ();

doe maar eens setVariable("policyid", "a{policyid}");

dit zal mogelijks nooit voorkomen, maar je houdt er toch beter rekening mee

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
schoene schreef op maandag 20 november 2006 @ 13:25:
Ik denk toch ook dat je beter niet pos++ doet, maar
pos += value.size ();

doe maar eens setVariable("policyid", "a{policyid}");

dit zal mogelijks nooit voorkomen, maar je houdt er toch beter rekening mee
Dat bedoelde ik dus :)

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
-NMe- schreef op maandag 20 november 2006 @ 08:44:
Hmm, ik heb zelf niet genoeg verstand van C++ om te zien of je wat verkeerd doet, maar het ziet ernaar uit dat de string niet netjes afgesloten wordt door een \0 na de replace,
std:;string heeft helemaal geen \0 nodig. \0 gebruik je in C, bij char*.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Goed punt schoene en farlane , die heb ik inmiddels al toegevoegd. Zou niet mogen gebeuren idd. Inmiddels is het probleem dus verder opgelost.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!

Pagina: 1