latajacaryba Temat założony przez niniejszego użytkownika |
[rapidxml] błędne odczytywanie wartości węzła przy zbyt długim pliku » 2021-08-02 01:08:28 Witam, mam problem z użyciem rapidxmla, mianowicie w pewnych sytuacjach (udało mi się jedynie ustalić, że przy pewnej długości pliku, a jest to paradoksalnie jedynie kilkanaście kb) wartości odczytywane z węzła są błędne. Nie jest to raczej wina błędnie sformatowanych danych, ponieważ plik składa się w głównej mierze z kilku powtarzających się "bloków" i nawet jeżeli są one takie same i odczyt poprzednich się udawał, to przy którymś jest w końcu problem. Wartości kolejnych węzłów (pobieranych przez next_sibling()) są poprawne, aż do trafienia na któryś z kolei. Sprawdzałem w pliku i nie odbiega on od normy, a jak wspomniałem, w innych blokach jego wartość była poprawnie odczytywana. przykład błędnie odczytanej wartości <RockSimDocument> <FileVersion>4</FileVersion> <DesignInformation> <RocketDesign> <Name>Rakieta</Name> <StageCount>1</StageCount> <DisplayFlags>7</DisplayFlags> <ViewType>0</ViewType> <ViewStageCount>3</ViewStageCount> <ViewTypeEdit>0</ViewTypeEdit> <ViewStageCountEdit>3</ViewStageCountEdit> <ZoomFactor>0.0</ZoomFactor> <ZoomFactorEdit>0.0</ZoomFactorEdit> <ScrollPosX>0</ScrollPosX> <ScrollPosY>0</ScrollPosY> <ScrollPosXEdit>0</ScrollPosXEdit> <ScrollPosYEdit>0</ScrollPosYEdit> <ThreeDFlags>0</ThreeDFlags> <ThreeDFlagsEdit>0</ThreeDFlagsEdit> <LastSerialNumber>8</LastSerialNumber> <Stage3Mass>0.0</Stage3Mass> <Stage2Mass>0.0</Stage2Mass> <Stage1Mass>0.0</Stage1Mass> <Stage3CG>417.5471832763761</Stage3CG> <Stage2CGAlone>0.0</Stage2CGAlone> <Stage1CGAlone>0.0</Stage1CGAlone> <Stage321CG>0.0</Stage321CG> <Stage32CG>0.0</Stage32CG> <CPCalcFlags>1</CPCalcFlags> <CPSimFlags>1</CPSimFlags> <UseKnownMass>0</UseKnownMass> <Stage3Parts> <NoseCone> <KnownMass>14.961414816532843</KnownMass> <Density>680.0</Density> <Material>Karton</Material> <Name>Głowica</Name> <KnownCG>0.0</KnownCG> <UseKnownCG>0</UseKnownCG> <Xb>0.0</Xb> <CalcMass>14.961414816532843</CalcMass> <CalcCG>101.96760546415946</CalcCG> <DensityType>0</DensityType> <RadialLoc>0.0</RadialLoc> <RadialAngle>0.0</RadialAngle> <LocationMode>0</LocationMode> <Len>150.00000000000003</Len> <FinishCode>2</FinishCode> <SerialNo>0</SerialNo> <ShapeCode>0</ShapeCode> <ConstructionType>1</ConstructionType> <WallThickness>2.0</WallThickness> <ShapeParameter>0.0</ShapeParameter> <AttachedParts/> <BaseDia>50.0</BaseDia> <ShoulderLen>0.0</ShoulderLen> <ShoulderOD>0.0</ShoulderOD> </NoseCone> <BodyTube> <KnownMass>41.016633685268374</KnownMass> <Density>680.0</Density> <Material>Karton</Material> <Name>Korpus rakiety</Name> <KnownCG>0.0</KnownCG> <UseKnownCG>0</UseKnownCG> <Xb>0.0</Xb> <CalcMass>41.016633685268374</CalcMass> <CalcCG>100.0</CalcCG> <DensityType>0</DensityType> <RadialLoc>0.0</RadialLoc> <RadialAngle>0.0</RadialAngle> <LocationMode>0</LocationMode> <Len>200.0</Len> <FinishCode>2</FinishCode> <SerialNo>1</SerialNo> <OD>50.0</OD> <ID>46.0</ID> <IsMotorMount>0</IsMotorMount> <MotorDia>46.0</MotorDia> <EngineOverhang>0.0</EngineOverhang> <IsInsideTube>0</IsInsideTube> <AttachedParts/> </BodyTube> Tutaj jest 7 identycznych węzłów <BodyTube> jak ten wyżej, stąd pominę ich dołączanie </Stage3Parts> <Stage2Parts/> <Stage1Parts/> </RocketDesign> </DesignInformation> </RockSimDocument>
|
|
pekfos |
» 2021-08-02 06:49:06 Masz błąd w kodzie, więc podaj jakiś kod. |
|
latajacaryba Temat założony przez niniejszego użytkownika |
» 2021-08-03 03:50:45 parsowanie i przesuwanie się po węzłach rapidxml::xml_document < > xmlDoc; try { xmlDoc.parse < 0 >( filedata.get() ); } catch( const rapidxml::parse_error & e ) { throw e; } currentElementNode = xmlDoc.first_node(); currentElementNode = currentElementNode->first_node( "DesignInformation" ); currentElementNode = currentElementNode->first_node( "RocketDesign" ); currentElementNode = currentElementNode->first_node( "Stage3Parts" );
Funkcje odpowiadające za odczytywanie wartości template < typename T > void RktReader::getNodeValue( const rapidxml::xml_node < > * const node, std::string_view nodeName, T & variable ) { auto * found = node->first_node( nodeName.data() ); if( found == nullptr ) return; const std::string_view view = found->value(); std::from_chars( view.data(), view.data() + view.size(), variable ); }
template < > inline void RktReader::getNodeValue < std::string >( const rapidxml::xml_node < > * const node, std::string_view nodeName, std::string & variable ) { auto * found = node->first_node( nodeName.data() ); variable = found->value(); }
Powyższe wykorzystuje ta funkcja oraz jej przeciążone w podklasach wersje void Element::Load( rapidxml::xml_node < > * node ) { RktReader & ref = RktReader::getInstance(); ref.getNodeValue( node, "KnownMass", knownMass ); ref.getNodeValue( node, "Density", density ); ref.getNodeValue( node, "Material", material ); ref.getNodeValue( node, "Name", name ); ref.getNodeValue( node, "Len", length ); }
Ogólny schemat: while( node != nullptr ) { std::shared_ptr < Element > newElem = nullptr; std::string_view elementName = node->name(); newElem->Generate( node ); node = node->next_sibling(); }
Generate jest metodą w której wywoływane jest wspomniane wyżej Load() Czasami wrzuca wyjątkiem, a innym razem (rzadziej) wykonuje się assert. Przed chwilą zaś pierwszy raz zadziałało, ale znów dzieje się to samo. assertexceptionEDIT: błąd pojawia się przy auto * found = node->first_node( nodeName.data() ); w getNodeValue() a konkretniej patrząc na call stack, to: getNodeValue >> first_node >> name_size |
|
pekfos |
» 2021-08-03 07:24:37 Najlepiej kompilujący się kod. Jak wstawiać kod na forum?W tym kodzie na razie nic się nie rzuca w oczy, poza tym że używasz std::string_view w niebezpieczny sposób. auto * found = node->first_node( nodeName.data() ); |
|
latajacaryba Temat założony przez niniejszego użytkownika |
» 2021-08-03 17:15:20 Kod sklada się z kilkudziesięciu plików, externali i ciężko byłoby go tu wrzucić, ale to nic, bo już wiem w czym był problem. Pierwszy wrzucony fragment kodu (jego część): rapidxml::xml_document < > xmlDoc;
obiekt jest lokalny, a currentElementNode odnosi się do nieistniejącego potem obiektu. Głupia rzecz a jednak. Co prawda nadal nie jestem pewien, dlaczego dla mniejszych plików wszystko działało, ale wygląda na to, że po wydłużeniu czasu życia xmlDoc wszystko działa. Z ciekawości, dlaczego w niebezpieczny sposób? |
|
pekfos |
» 2021-08-03 17:54:31 obiekt jest lokalny, a currentElementNode odnosi się do nieistniejącego potem obiektu. Czyli jak zwykle problem był w niepodanym fragmencie kodu. Co prawda nadal nie jestem pewien, dlaczego dla mniejszych plików wszystko działało Kwestia ilości używanej pamięci. Dla mniejszych plików po prostu trudniej zreprodukować problem, dlatego "działało". Z ciekawości, dlaczego w niebezpieczny sposób? Zakładasz że string view wskazuje na napis zakończony zerem, a cały sens używania string view jest w tym, by napisy nie musiały takie być. Jak chcesz robić oszczędności, to już lepiej przekaż ten napis przez wskaźnik, wtedy wiadomo czego się po tym spodziewać i to jest też forma jakiej potrzebujesz w implementacji. const std::string_view view = found->value(); std::from_chars( view.data(), view.data() + view.size(), variable ); Skoro już mowa o oszczędnościach, to rapidxml pozwala pobrać długość napisu, więc string view jest zbędne, zwłaszcza że musi samemu obliczyć tą długość. |
|
latajacaryba Temat założony przez niniejszego użytkownika |
» 2021-08-03 19:14:37 Ok, dzięki za pomoc i wyjaśnienia, zamykam |
|
« 1 » |