I'm using QXmlStreamReader and QXmlStreamWriter to read and write XML files in my application.
My example XML file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<Senders>
<Sender>
<Name>COMPANY XYZ</Name>
<Street>Random</Street>
<BuildingNumber>23D</BuildingNumber>
<LocalNumber>123</LocalNumber>
<CityCode>3434-21</CityCode>
<City>New York</City>
</Sender>
</Senders>
It was written with my code. Basically, I do not have problem with writing XML files but have some troubles trying to read them after writing. Here's my code, which prints out empty values (XML file is not empty).
#include "mainwindow.h"
#include <QApplication>
#include <QtCore>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFile file("../data.xml");
if(file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("Senders");
xmlWriter.writeStartElement("Sender");
xmlWriter.writeTextElement("Name", "COMPANY XYZ");
xmlWriter.writeTextElement("Street", "Random");
xmlWriter.writeTextElement("BuildingNumber", "23D");
xmlWriter.writeTextElement("LocalNumber", "123");
xmlWriter.writeTextElement("CityCode", "3434-21");
xmlWriter.writeTextElement("City", "New York");
xmlWriter.writeEndElement();
xmlWriter.writeEndElement();
file.close();
}
QVector<QString> data;
if(file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QXmlStreamReader xmlReader(&file);
while (!xmlReader.atEnd() && !xmlReader.hasError())
{
xmlReader.readNext();
if (xmlReader.isStartElement())
{
qDebug() << "element name: '" << xmlReader.name().toString() << "'"
<< ", text: '" << xmlReader.text().toString() << "'" << endl;
}
}
file.close();
}
return a.exec();
}
So what I'm doing wrong while reading my file?
The output I got:
element name: ' "Senders" ' , text: ' "" '
element name: ' "Sender" ' , text: ' "" '
element name: ' "Name" ' , text: ' "" '
element name: ' "Street" ' , text: ' "" '
element name: ' "BuildingNumber" ' , text: ' "" '
element name: ' "LocalNumber" ' , text: ' "" '
element name: ' "CityCode" ' , text: ' "" '
element name: ' "City" ' , text: ' "" '
When you are at StartElement token, you can only get this element's name, not it's value. You need to read further and get to Characters token to be able to read the element's value. You would also want to skip whitespace-only tokens:
while (!xmlReader.atEnd() && !xmlReader.hasError())
{
xmlReader.readNext();
//here we are at StartElement, so we can read the element's name
if (xmlReader.isStartElement())
{
qDebug() << "element name: '" << xmlReader.name().toString() << "'";
}
//here we are inside the element, so if it is not empty, we can read the element's value
else if(xmlReader.isCharacters() && !xmlReader.isWhitespace())
{
qDebug() << "element value: '" <<xmlReader.text().toString() << "'";
}
}
Related
I am trying to extract a ragne of information from the data.
for example,
[student1]
id: 001
name:red
surname:brown
phone number:0123456
address: blabla
from this data (it's text file), I want to extract all information of this student using regular expression, just by putting in student number 001.
here is the code i tried.
ifstream file (filename);
string line;
bool found = false;
regex format("[0-9]{3}");
regex name("^name:");
if (regex_search(id, format)) {
regex Id("^id: "+id);
if (file.is_open()) {
while ( getline (file,line)) {
if (regex_search(line, Id)) {
found = true;
} else if ( found && regex_search(line, name) ) {
return line;
break;
}
}
} else {
return "Sorry, can not open file.";
}
file.close();
}
return("");
}
so far, I succeded to extract specific information like name or surname etc.. but not every information.
My idea is modifing the part of regex name with bracket "[]" to extract a range of string on that code. but...failed.
You can read the file and use the following regular expression on the content. With regex_search you can search for the student and iterate over the groups.
You can use this regex to find the student and extract its data:
\[student\w+\]\nid: *001\nname: *(\w*)\nsurname: *(\w*)\nphone number: *(\w*)\naddress: *(\w*)
E.g. for
[student0]
id: 000
name:red
surname:brown
phone number:0123456
address: blabla
[student1]
id: 001
name:red
surname:brown
phone number:0123456
address: blabla
[student2]
id: 002
name:red
surname:brown
phone number:0123456
address: blabla
it will match
[student1]
id: 001
name:red
surname:brown
phone number:0123456
address: blabla
and the groups contain
red, brown, 0123456 and blabla.
A good online tool to work with regular expressions is https://regexr.com/5891j
You can assign the id you are looking for to variable id and build the regex string with:
R"(\[student\w+\]\nid: *)"s + id + R"(\nname: *(\w*)\nsurname: *(\w*)\nphone number: *(\w*)\naddress: *(\w*))"s;
An example code:
#include <fstream>
#include <iostream>
#include <regex>
#include <string>
using std::literals::string_literals::operator""s;
int main() {
std::string id = "001";
std::regex regex((R"(\[student\w+\]\nid: *)"s + id + R"(\nname: *(\w*)\nsurname: *(\w*)\nphone number: *(\w*)\naddress: *(\w*))"s).c_str());
std::ifstream file("data.txt");
if (!file) return EXIT_FAILURE;
std::string content{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
std::smatch matches;
if (std::regex_search(content, matches, regex)) {
std::cout << "Student data:\n" << matches[0] << "\n\n";
std::cout << "Id: " << id << '\n';
std::cout << "Name: " << matches[1] << '\n';
std::cout << "Surname: " << matches[2] << '\n';
std::cout << "Phone: " << matches[3] << '\n';
std::cout << "Address: " << matches[4] << '\n';
}
}
Example output:
Student data:
[student1]
id: 001
name:red
surname:brown
phone number:0123456
address: blabla
Id: 001
Name: red
Surname: brown
Phone: 0123456
Address: blabla
i'm trying to get process name from Win32_Processor soo i create function ProcessNameInfo.
my code is :
void ProcessNameInfo(string nameoption, String^ hardwareClass, String^ propertyNname)
{
ManagementObjectSearcher^ searcher = gcnew ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM " + hardwareClass);
ManagementObjectCollection^ collection = searcher->Get();
for each (ManagementObject^ object in collection)
{
String ^ s = object[propertyNname]->ToString();
CString str3(s);
const TCHAR* x = (LPCTSTR)str3;
char szString[MAX_LENGTH];
size_t nNumCharConverted;
wcstombs_s(&nNumCharConverted, szString, MAX_LENGTH,
x, MAX_LENGTH);
std::string HardwareInfo = szString;
std::transform(HardwareInfo.begin(), HardwareInfo.end(), HardwareInfo.begin(), [](char ch) {
return ch == ' ' ? '_' : ch;
});
file << "\"" << nameoption << "\": \"" << HardwareInfo << "\"," << endl;
}
}
in the main i call this function like this :
ProcessNameInfo("Processor", "Win32_Processor", "Name");
for some devices it's working but in another devices i get error Unhandled exception:
System.NullReferenceException object reference not set to an instance of an object in ProcessNameInfo(basic_string<char\,std::char_traites<char>\,std::allocator<char> >* nameoption, String hardwareClass, String propertyNname) in main() _mainCRTStartup()
The problem is I don't know the length of the input string.
My function can only replace if the input string is "yyyy". I think of the solution is that first, we will try to convert the input string back to "yyyy" and using my function to complete the work.
Here's my function:
void findAndReplaceAll(std::string & data, std::string toSearch, std::string replaceStr)
{
// Get the first occurrence
size_t pos = data.find(toSearch);
// Repeat till end is reached
while( pos != std::string::npos)
{
// Replace this occurrence of Sub String
data.replace(pos, toSearch.size(), replaceStr);
// Get the next occurrence from the current position
pos = data.find(toSearch, pos + replaceStr.size());
}
}
My main function
std::string format = "yyyyyyyyyydddd";
findAndReplaceAll(format, "yyyy", "%Y");
findAndReplaceAll(format, "dd", "%d");
My expected output should be :
%Y%d
Use regular expressions.
Example:
#include <iostream>
#include <string>
#include <regex>
int main(){
std::string text = "yyyyyy";
std::string sentence = "This is a yyyyyyyyyyyy.";
std::cout << "Text: " << text << std::endl;
std::cout << "Sentence: " << sentence << std::endl;
// Regex
std::regex y_re("y+"); // this is the regex that matches y yyy or more yyyy
// replacing
std::string r1 = std::regex_replace(text, y_re, "%y"); // using lowercase
std::string r2 = std::regex_replace(sentence, y_re, "%Y"); // using upercase
// showing result
std::cout << "Text replace: " << r1 << std::endl;
std::cout << "Sentence replace: " << r2 << std::endl;
return 0;
}
Output:
Text: yyyyyy
Sentence: This is a yyyyyyyyyyyy.
Text replace: %y
Sentence replace: This is a %Y.
If you want to make it even better you can use:
// Regex
std::regex y_re("[yY]+");
That will match any mix of lowercase and upper case for any amount of 'Y's .
Example output with that Regex:
Sentence: This is a yYyyyYYYYyyy.
Sentence replace: This is a %Y.
This is just a simple example of what you can do with regex, I'd recommend to look at the topic on itself, there is plenty of info her in SO and other sites.
Extra:
If you want to match before replacing to alternate the replacing you can do something like:
// Regex
std::string text = "yyaaaa";
std::cout << "Text: " << text << std::endl;
std::regex y_re("y+"); // this is the regex that matches y yyy or more yyyy
std::string output = "";
std::smatch ymatches;
if (std::regex_search(text, ymatches, y_re)) {
if (ymatches[0].length() == 2 ) {
output = std::regex_replace(text, y_re, "%y");
} else {
output = std::regex_replace(text, y_re, "%Y");
}
}
Class prints a string to the console.
How to make the width of output lines is equal characterWidth = 40,
ie after 40 characters was transferred to a new line?
#include <string>
#include <iostream>
class StringProcessing {
public:
StringProcessing() : characterWidth(40),
textToBeFormatted("NULL") {}
inline void StringProcessing::initString() {
textToBeFormatted =
"text some text some text some text some text some text"
"text some text some text some text some text some text"
"text some text some text some text some text"
"text some text some text some text some text some text"
"text some text some text some text some text some text";
}
inline void displayString()
{ std::cout << textToBeFormatted << std::endl; }
private:
int characterWidth;
std::string textToBeFormatted;
};
I have an idea but here the words in the console are cut off, so they need to be transferred to the next line and perform a width alignment
inline void displayString()
{
const std::string& s = textToBeFormatted;
for (int i = 0; i < s.length() / 40 + (bool)(s.length() % 40); ++i)
{
std::cout << std::left
<< std::setfill(' ')
<< std::setw(40)
<< s.substr(i * 40, 40)
<< std::endl;
}
}
Here is the answer suitable for me
#include <string>
#include <iostream>
#include <iomanip>
class StringProcessing
{
public:
StringProcessing() : characterWidth(40),
textToBeFormatted("NULL") {}
inline void initString() {
textToBeFormatted = "text some text some text some text some text some text"
"text some text some text some text some text some text"
"text some text some text some text some text some text"
"text some text some text some text some text some text"
"text some text some text some text some text some text";
}
inline void displayString()
{
const std::string& s = textToBeFormatted;
const int& width = characterWidth;
for (int current = 0; current < s.length();)
{
if (s.length() < width)
{
output(s);
break;
}
if (s.length() - current < width)
{
output(s.substr(current));
break;
}
std::string substr = s.substr(current, width);
current += width;
size_t space = substr.rfind(' ');
if (space != std::string::npos && (substr[width - 1] != ' ' &&
(s.length() > current && s[current] != ' ')))
{
current -= width - space - 1;
substr = substr.substr(0, space + 1);
}
output(substr);
}
}
private:
inline void output(const std::string& s)
{
std::cout << setfill(' ') << std::right << std::setw(characterWidth) << s << std::endl;
}
int characterWidth;
std::string textToBeFormatted;
};
I have a file, each line holds a .
<div style="random properties" id="keyword1:string id:int">text</div>
<div style="random properties" id="keyword1:string id:int">text</div>
<div style="random properties" id="keyword2:string id:int">text</div>
<div style="random properties" id="keyword2:string id:int">text</div>
Can i with fscanf return a list of text and id for a matching keyword1 and keyword2?
You can simply read it with regex:
std::string s;
std::regex r( "<div style=\"[^\"]*\" id=\".*(\\d+)\">((?:(?!</div>).)*)</div>" );
while( std::getline(in, s) ) {
std::smatch m;
if( std::regex_match(s, m, r) ) {
std::cout << "id = " << m.str(1) << ", text = " << m.str(2) << std::endl;
} else {
std::cout << "invalid pattern" << std::endl;
}
}
But if you want to read more about regex please go to http://en.cppreference.com/w/cpp/regex