QString::replace doesnt replace - c++

Problem
I am trying to convert a string to a C string. In doing so, I need to replace " with \". I use the following code to do this:
QString Converter::plain2C(const QString &in) {
QString out;
// Split input in each line
QStringList list = in.split(QChar('\n'));
for (int i = 0; list.length() > i; i++) { // Go throught each line of the input
QString line = list[i]; // Thats the line
line.replace(QChar('\\'), QLatin1String("\\\\")); // replace \ with \\
line.replace(QChar('"'), QLatin1String("\\\"")); // replace " with \"
// For printf()
if(escapePercent)
line.replace(QChar('%'), QLatin1String("%%"));
// If option "Split output into multiple lines" is active add a " to the output
if (multiLine)
out.append(QChar('"'));
// append the line to the output
out.append(line);
// append a "\n" to the output because we are at the end of a line
if (list.length() -1 > i)
out.append(QLatin1String("\\n"));
// If option "Split output into multiple lines" is active add a " and \n to the output
if (multiLine) {
out.append(QChar('"'));
out.append(QChar('\n'));
}
}
if (!multiLine) {
out.prepend(QChar('"'));
out.append(QChar('"'));
}
return out;
}
However, " is still there without a \ before.
Information
Qt Version 5.15.3
C++17
Edit
The application is used to enter a normal string copied from the Internet and get as a result a string that can be copied into a C/C++ program.
More code
void Converter::run()
{
if (_from != From::NotSupportet &&
_to != To::toInvalid) {
QString out;
// More code obove and below
else if (_from == From::Plain) {
switch (_to) {
case To::toCString:
out = plain2C(_in);
break;
// Emits the ready string which is applied direct to a QPlainTextEdit
emit htmlReady(out);
}
}
Edit 2
Added more comments to the code

It works now. The problem was the line above:
line.replace(QChar('\'), QLatin1String("\\\\")); // replace \ with \\
The problem was that the comment ended with 2 \. That somehow disabled the next line or something like that.
Anyway, this is the working code:
QString Converter::plain2C(const QString &in) {
QString out;
// Split input in each line
QStringList list = in.split(QChar('\n'));
for (int i = 0; list.length() > i; i++) { // Go throught each line of the input
QString line = list[i]; // Thats the line
line.replace(QChar('\\'), QLatin1String("\\\\")); // replace "\" with "\\"
line.replace(QChar('"'), QLatin1String("\\\"")); // replace " with \"
// For printf()
if(escapePercent)
line.replace(QChar('%'), QLatin1String("%%"));
// If option "Split output into multiple lines" is active add a " to the output
if (multiLine)
out.append(QChar('"'));
// append the line to the output
out.append(line);
// append a "\n" to the output because we are at the end of a line
if (list.length() -1 > i)
out.append(QLatin1String("\\n"));
// If option "Split output into multiple lines" is active add a " and \n to the output
if (multiLine) {
out.append(QChar('"'));
out.append(QChar('\n'));
}
}
if (!multiLine) {
out.prepend(QChar('"'));
out.append(QChar('"'));
}
return out;
}

Related

CppUnitTestFramework: Test Method Fails, Stack Trace Lists Line Number at the End of Method, Debug Test Passes

I know, I know - that question title is very much all over the place. However, I am not sure what could be an issue here that is causing what I am witnessing.
I have the following method in class Project that is being unit tested:
bool Project::DetermineID(std::string configFile, std::string& ID)
{
std::ifstream config;
config.open(configFile);
if (!config.is_open()) {
WARNING << "Failed to open the configuration file for processing ID at: " << configFile;
return false;
}
std::string line = "";
ID = "";
bool isConfigurationSection = false;
bool isConfiguration = false;
std::string tempID = "";
while (std::getline(config, line))
{
std::transform(line.begin(), line.end(), line.begin(), ::toupper); // transform the line to all capital letters
boost::trim(line);
if ((line.find("IDENTIFICATIONS") != std::string::npos) && (!isConfigurationSection)) {
// remove the "IDENTIFICATIONS" part from the current line we're working with
std::size_t idStartPos = line.find("IDENTIFICATIONS");
line = line.substr(idStartPos + strlen("IDENTIFICATIONS"), line.length() - idStartPos - strlen("IDENTIFICATIONS"));
boost::trim(line);
isConfigurationSection = true;
}
if ((line.find('{') != std::string::npos) && isConfigurationSection) {
std::size_t bracketPos = line.find('{');
// we are working within the ids configuration section
// determine if this is the first character of the line, or if there is an ID that precedes the {
if (bracketPos == 0) {
// is the first char
// remove the bracket and keep processing
line = line.substr(1, line.length() - 1);
boost::trim(line);
}
else {
// the text before { is a temp ID
tempID = line.substr(0, bracketPos - 1);
isConfiguration = true;
line = line.substr(bracketPos, line.length() - bracketPos);
boost::trim(line);
}
}
if ((line.find("PORT") != std::string::npos) && isConfiguration) {
std::size_t indexOfEqualSign = line.find('=');
if (indexOfEqualSign == std::string::npos) {
WARNING << "Unable to determine the port # assigned to " << tempID;
}
else {
std::string portString = "";
portString = line.substr(indexOfEqualSign + 1, line.length() - indexOfEqualSign - 1);
boost::trim(portString);
// confirm that the obtained port string is not an empty value
if (portString.empty()) {
WARNING << "Failed to obtain the \"Port\" value that is set to " << tempID;
}
else {
// attempt to convert the string to int
int workingPortNum = 0;
try {
workingPortNum = std::stoi(portString);
}
catch (...) {
WARNING << "Failed to convert the obtained \"Port\" value that is set to " << tempID;
}
if (workingPortNum != 0) {
// check if this port # is the same port # we are publishing data on
if (workingPortNum == this->port) {
ID = tempID;
break;
}
}
}
}
}
}
config.close();
if (ID.empty())
return false;
else
return true;
}
The goal of this method is to parse any text file for the ID portion, based on matching the port # that the application is publishing data to.
Format of the file is like this:
Idenntifications {
ID {
port = 1001
}
}
In a separate Visual Studio project that unit tests various methods, including this Project::DetermineID method.
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
TEST_CLASS(ProjectUnitTests) {
Project* parser;
std::string projectDirectory;
TEST_METHOD_INITIALIZE(ProjectUnitTestInitialization) {
projectDirectory = EXPAND(UNITTESTPRJ);
projectDirectory.erase(0, 1);
projectDirectory.erase(projectDirectory.size() - 2);
parser = Project::getClass(); // singleton method getter/initializer
}
// Other test methods are present and pass/fail accordingly
TEST_METHOD(DetermineID) {
std::string ID = "";
bool x = parser ->DetermineAdapterID(projectDirectory + "normal.cfg", ID);
Assert::IsTrue(x);
}
};
Now, when I run the tests, DetermineID fails and the stack trace states:
DetermineID
Source: Project Tests.cpp line 86
Duration: 2 sec
Message:
Assert failed
Stack Trace:
ProjectUnitTests::DetermineID() line 91
Now, in my test .cpp file, TEST_METHOD(DetermineID) { is present on line 86. But that method's } is located on line 91, as the stack trace indicates.
And, when debugging, the unit test passes, because the return of x in the TEST_METHOD is true.
Only when running the test individually or running all tests does that test method fail.
Some notes that may be relevant:
This is a single-threaded application with no tasks scheduled (no race condition to worry about supposedly)
There is another method in the Project class that also processes a file with an std::ifstream same as this method does
That method has its own test method that has been written and passes without any problems
The test method also access the "normal.cfg" file
Yes, this->port has an assigned value
Thus, my questions are:
Why does the stack trace reference the closing bracket for the test method instead of the single Assert within the method that is supposedly failing?
How to get the unit test to pass when it is ran? (Since it currently only plasses during debugging where I can confirm that x is true).
If the issue is a race condition where perhaps the other test method is accessing the "normal.cfg" file, why does the test method fail even when the method is individually ran?
Any support/assistance here is very much appreciated. Thank you!

If statement fails with regex comparison

public list[str] deleteBlockComments(list[str] fileLines)
{
bool blockComment = false;
list[str] sourceFile = [];
for(fileLine <- fileLines)
{
fileLine = trim(fileLine);
println(fileLine);
if (/^[\t]*[\/*].*$/ := fileLine)
{
blockComment = true;
}
if (/^[\t]*[*\/].*$/ := fileLine)
{
blockComment = false;
}
println(blockComment);
if(!blockComment)
{
sourceFile = sourceFile + fileLine;
}
}
return sourceFile;
}
For some reason, I am not able to detect /* at the beginning of a string. If I execute this on the command line, it seems to work fine.
Can someone tell me what I am doing wrong? In the picture below you can see the string to be compared above the comparison result (false).
[\/*] is a character set that matches forward slash or star, not both one after the other. Simply remove the square brackets and your pattern should start behaving as you expect.
While we're at it, let's also get rid of the superfluous square brackets around \t
^\t*\/*.*$

Firemonkey: Setting TLabel Text causes String Index out of Range Exception

I have a method which builds a help message and then sets a TLabel's text property to that help message. However, whenever I attempt to set the text of the label, I get the following exception:
'String index out of range. (-1) Must be >=0 and <=42'
The method is as follows:
void __fastcall TPasswordChangeForm::BuildHelpMessage()
{
String HelpMsg = "";
if( NewPassEdit->Text.Length() < MinPasswordLength )
{
HelpMsg += "Password length too short.";
}
else
{
HelpMsg += "Password length OK.";
}
HelpMsg += "\n";
if( NewPassEdit->Text == ConfirmPassEdit->Text )
{
HelpMsg += "Passwords match.";
}
else
{
HelpMsg += "Passwords do not match.";
}
ShowMessage( HelpMsg ); //added for debugging, shows string as expected
HelpLabel->Text = HelpMsg; //exception thrown here
}
I added a ShowMessage call just to check the value of my string. It shows up just fine. I am also able to set the label to be any arbitrary value such as:
HelpLabel->Text = "This message works!";
Am I doing something wrong as I build the HelpMsg String?
Edit: Commenting out the line which adds the \n to the String fixes the problem. Similarly, the following code will cuase the exception:
String test = "this is a test";
test += "\n";
test += "test 2";
HelpLabel->Text = test;
What is it about the \n that causes issues? How do I correctly add a new line?
Currently updating to new new C++ Builder so cant play around with your snippet. I have method's for handling error messages which are output to a log tab as opposed to the ShowMessage, rather than using a string though I use a TStringList. For example:
void __fastcall TPasswordChangeForm::BuildHelpMessage()
{
TStringList HelpMsg = new TStringList(this);
if( NewPassEdit->Text.Length() < MinPasswordLength )
{
HelpMsg->Add("Password length too short.");
}
else
{
HelpMsg->Add("Password length OK.");
}
if( NewPassEdit->Text == ConfirmPassEdit->Text )
{
HelpMsg->Add("Passwords match.");
}
else
{
HelpMsg->Add("Passwords do not match."_;
}
ShowMessage(HelpMsg->Text); //added for debugging, shows string as expected
HelpLabel->Text = HelpMsg->Text; //exception thrown here
}
By using TStringList or TStrings (parent) when you access the Text property the strings within the object are output, each separated by a carriage return and line feed.
See the TStringList Docs here - hope this is of some help!

QSettings: How to read array from INI file

I wanna read comma separated data form INI file. I've already read here:
QSettings::IniFormat values with "," returned as QStringList
How to read a value using QSetting if the value contains comma character
...that commas are treated as separators and QSettings value function will return QStringList.
However, my data in INI file looks like this:
norm-factor=<<eof
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
eof
I don't need a whole matrix. All rows joined up together are fair enough for me. But can QSettings handle such structure?
Should I read this using:
QStringList norms = ini->value("norm-factor", QStringList()).toStringList();
Or do I have to parse it in another way?
The line breaks are a problem since INI files use line breaks for their own syntax.
Qt seems to not support your type of line continuation (<<eol ... eol).
QSettings s("./inifile", QSettings::IniFormat);
qDebug() << s.value("norm-factor");
yields
QVariant(QString, "<<eof")
The <<eol expression might be invalid INI in itself. (Wikipedia on INI files)
I suggest you parse the file manually.
Ronny Brendel's answer is correct ...i am only adding code that solves above problem ...it creates temporary INI file with corrected arrays:
/**
* #param src source INI file
* #param dst destination (fixed) INI file
*/
void fixINI(const QString &src, const QString &dst) const {
// Opens source and destination files
QFile fsrc(src);
QFile fdst(dst);
if (!fsrc.open(QIODevice::ReadOnly)) {
QString msg("Cannot open '" + src + "'' file.");
throw new Exception(NULL, msg, this, __FUNCTION__, __LINE__);
}
if (!fdst.open(QIODevice::WriteOnly)) {
QString msg("Cannot open '" + dst + "'' file.");
throw new Exception(NULL, msg, this, __FUNCTION__, __LINE__);
}
// Stream
QTextStream in(&fsrc);
QTextStream out(&fdst);
bool arrayMode = false;
QString cache;
while (!in.atEnd()) {
// Read current line
QString line = in.readLine();
// Enables array mode
// NOTE: Clear cache and store 'key=' to it, without '<<eof' text
if (arrayMode == false && line.contains("<<eof")) {
arrayMode = true;
cache = line.remove("<<eof").trimmed();
continue;
}
// Disables array mode
// NOTE: Flush cache into output and remove last ',' separator
if (arrayMode == true && line.trimmed().compare("eof") == 0) {
arrayMode = false;
out << cache.left(cache.length() - 1) << "\n";
continue;
}
// Store line into cache or copy it to output
if (arrayMode) {
cache += line.trimmed() + ",";
} else {
out << line << "\n";
}
}
fsrc.close();
fdst.close();
}

Partial line from cpp file ending up in output file - haunted code?

I'm sorry, it would be extremely difficult to make a fully reproducible version of the error --- so please bare with my schematic code.
This program retrieves information from a web page, processes it, and saves output to an ASCII file. I also have a 'log' file (FILE *theLog---contained within a Manager object) for reporting errors, etc.
Some background methods:
// Prints string to log file
void Manager::logEntry(const string lstr) {
if( theLog != NULL ) { fprintf(theLog, "%s", lstr.c_str()); }
}
// Checks if file with given name already exists
bool fileExists(const string fname) {
FILE *temp;
if( temp = fopen(fname.c_str(), "r") ) {
fclose(temp);
return true;
} else { return false; }
}
// Initialize file for writing (some components omitted)...
bool initFile(FILE *&oFile, const string fname) {
if(oFile = fopen(fname.c_str(), "w") ) { return true; }
else { return false; }
}
The stuff causing trouble:
// Gets data from URL, saves to file 'dataFileName', input control flag 'foreCon'
// stu is some object that has string which i want
bool saveData(Manager *man, Stuff *stu, string dataFileName, const int foreCon) {
char logStr[CHARLIMIT_LARGE]; // CHARLIMIT_LARGE = 2048
sprintf(logStr, "Saving Data...\n");
man->logEntry( string(logStr) ); // This appears fine in 'theLog' correctly
string data = stu->getDataPrefixStr() + getDataFromURL() + "\n"; // fills 'data' with stuff
data += stu->getDataSuffixStr();
if( fileExists(dataFileName) ) {
sprintf(logStr, "save file '%s' already exists.", dataFileName.c_str() );
man->logEntry( string(logStr) );
if( foreCon == -1 ) {
sprintf(logStr, "foreCon = %d, ... exiting.", foreCon); // LINE 'A' : THIS LINE ENDS UP IN OUTPUT FILE
tCase->logEntry( string(logStr) );
return false;
} else {
sprintf(logStr, "foreCon = %d, overwriting file.", foreCon); // LINE 'B' : THIS LINE ENDS UP IN LOG FILE
tCase->logEntry( string(logStr) );
}
}
// Initialize output file
FILE *outFile;
if( !initFile(outFile, dataFileName) ) {
sprintf(logStr, "couldn't initFile '%s'", dataFileName.c_str());
tCase->logEntry( string(logStr) );
return false;
}
fprintf(outFile, "%s", data.c_str()); // print data to output file
if( fclose(outFile) != EOF) {
sprintf(logStr, "saved to '%s'", dataFileName.c_str());
tCase->logEntry( string(logStr) );
return true;
}
return false;
}
If the file already exists, AND 'int foreCon = -1' then the code should print out line 'A' to the logFile. If the file exists and foreCon != -1, the old file is overwritten with data. If the file doesn't exist, it is created, and the data is written to it.
The result however, is that a broken up version of line 'A' appears in the data file AND line 'B' is printed in the log file!!!!
What the data file looks like:
.. exiting.20130127 161456
20130127 000000,55,17,11,0.00
20130127 010000,54,17,11,0.00
... ...
The second line and onward look correct, but there is an extra line that contains part of line 'A'.
Now, the REALLY WEIRD PART. If I comment out everything in the if( foreCon == -1) { ... } block, then the data file looks like:
%d, ... exiting.20130127 161456
20130127 000000,55,17,11,0.00
20130127 010000,54,17,11,0.00
... ...
There is still an extra line, but it is the LITERAL CODE copied into the data file.
I think there is a poltergeist in my code. I don't understand how any of this could happen.
Edit: I've tried printing to console the data string, and it gives the same messed up values: i.e. %d, ... exiting.20130127 161456 - so it must be something about the string instead of the FILE *
Answer based on your latest comment:
getDataPrefixStr() ends up returning a string which starts with
something like string retStr = COMCHAR + " file created on ..."; such
that const char COMCHAR = '#';. Could the COMCHAR be the problem??
You can't add characters and string literals (which are arrays of char, not strings) like that.
You're adding 35 (the ASCII for "#") to the address of " file created on ... ", i.e. getDataPrefixStr() is whatever starts 35 characters from the start of that string. Since all literal strings are stored together in the same data area, you'll get strings from the program in the output.
Instead, you cold do
const string COMCHAR = "*";
string retStr = COMCHAR + " file created on ...";
It could be that logStr is too short and that it is causing data to be overwritten in other buffers (did you double check CHARLIMIT_LARGE?). You can diagnose this by commenting all writes to logStr (sprintf) and see if data is still corrupted. In general, your code is vulnerable to this if a user can set dataFileName (to be a very long string); use snprintf or ostringstream instead.
Otherwise, I would guess that either stu->getDataPrefixStr() or getDataFromURL() are returning corrupted results or return type char* instead of string. Try printing these values to the console directly to see if they are corrupted or not. If they return a char*, then data = stu->getDataPrefixStr() + getDataFromURL() will have undefined behavior.
if( temp = fopen(fname.c_str(), 'r') ) {
should be
if( temp = fopen(fname.c_str(), "r") ) {