QTestLib results different from program output - qt

I have some simple code that reverses a QString.
const QString reverse_qstring(const QString& str_in)
{
QString out;
Q_FOREACH(const QChar c, str_in) {
out.push_front(c);
}
return out;
}
When I input text from the command line with non-ASCII characters, things go as expected:
"¿como estás?" : "?sátse omoc¿"
However, when I make the following unit test (Using QTestLib):
QCOMPARE(reverse_qstring(QString("¿como estás?")), QString("?sátse omoc¿"));
I get:
FAIL! : program::test_qstring() Compared values are not the same
Actual (reverse_qstring(QString("??como est??s?"))): ?s??tse omoc??
Expected (QString("?s??tse omoc??")): ?s??tse omoc??
Any ideas?

I think you can set the codec for Utf8: http://doc.qt.io/qt-4.8/qtextcodec.html#setCodecForCStrings
Or you can use this instead: QString::fromUtf8("?sátse omoc¿")

Related

How to get QString qDebug output as string?

Let's have a look at this little application:
#include <QString>
#include <QDebug>
int main(int argc, char *argv[]) {
const auto test_string =
QString{"Some string \n \x01 \u0002 with some \r special chars"};
qDebug() << test_string;
qDebug(qPrintable(test_string));
}
It gives the following output:
"Some string \n \u0001 \u0002 with some \r special chars"
Some string
special chars
Press <RETURN> to close this window...
This demonstrates how the qDebug << operator comes with some functionality that converts all the special characters of a QString to some readable string, which can easily be put in a string declaration in C++.
I would like to use this functionality to feed strings into a custom logging framework. Is there a possibility to use the same conversion function directly?
Effectively, this would mean to convert test_string to a QString instance that gives the same output on both the above qDebug statements.
I had the same question and I did not found the complete answer (yet). However, I found QVariant which allows you to call toString() on the most basic C and Qt types:
QVariant("foo").toString(); // "foo"
QVariant(true).toString(); // "true"
QVariant(QDateTime("2020-11-28")).toString(); // "2020-11-28"
Then you could wrap this into one method:
QString variantToString(const QVariant variant) {
return (variant.userType() != QMetaType::QString
&& variant.canConvert(QMetaType::QStringList))
? "(" + variant.toStringList().join(", ") + ")"
: variant.toString();
}
variantToString(42); // "42" // works due to implicit cast
You could do a few extra checks for non-stringifiable types (see also canConvert() and userType(), e.g. lists, regular expressions or whatever you need, but I totally agree that it would be nicer to reuse Qt's own logging functions instead ...
I had a similar issue but I wanted to use my custom operator<< overload that was defined for QDebug, therefore, I did the following:
// This could be anything that provides an 'operator<<' overload.
QString value = "Hello, world!";
QString result;
QDebug{ &result } << value;

Qt logical error during getting user's input from terminal via getline function and writing it into a file

Using console, I want to write the desired user's input into a file via getline function inside the wFile function and then read it. I face with logical error during Runtime; whatever I as user write doesn't type into the output terminal and it doesn't succeed more steps. Apparently fwrite function with this feature in the libraries exists, but I want to write my own code differently this way. I think I must have neglected a point. Here's the code:
#include <QCoreApplication>
#include <QDebug>
#include <QFile>
#include <QString>
#include <QTextStream>
#include <String>
#include <cstdlib>
using namespace std;
void wFile(QString Filename)
{
QFile mFile(Filename);
QTextStream str(&mFile);
qDebug() << "what do you want to write in the desired file: ";
istream& getline (istream& is, string& str);
if (!mFile.open(QFile::WriteOnly | QFile::Text))
{
qDebug() << "could not open the file";
return;
}
mFile.flush();
mFile.close();
}
void read (QString Filename){
QFile nFile(Filename);
if(!nFile.open(QFile::ReadOnly | QFile::Text))
{
qDebug() << "could not open file for reading";
return;
}
QTextStream in(&nFile);
QString nText = in.readAll();
qDebug() << nText;
nFile.close();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString nFilename ="P:/DocumentArminV.txt";
wFile(nFilename);
read(nFilename);
return a.exec();
}
Spoiler alarm: At the very end of this answer, there is a very simple recommendation for a fix.
What OP did
Concerning istream& getline (istream& is, string& str); in wFile(QString Filename):
This declares function getline() in function wFile().
This is a valid declaration concerning C++.
Concerning the sample code, I missed the respective headers. IMHO,
#include <istream> and
#include <string>
are required to make this compiling.
However, it is possible that the existing #includes include them indirectly. So, OP's code may even compile without them.
Declaring functions, which are not used as well as re-declaring functions which are already declared is somehow useless but not wrong.
To demonstrate this, I made a small sample:
#include <cstdio>
#include <istream>
#include <string>
void func()
{
puts("in func()\n");
std::istream& getline(std::istream&, std::string&);
// Even duplicated prototyping is accepted without complaints:
std::istream& getline(std::istream&, std::string&);
}
int main ()
{
func();
return 0;
}
compiles and runs perfectly.
Output:
in func()
Live Demo on coliru
What OP (probably) wanted
Using console, I want to write the desired user's input into a file via getline function inside the wFile function and then read it.
This sounds a bit confusing to me. std::getline(std::cin, ) can be used to read user input from console. May be, it's a bit bad wording only.
Assuming, the OP wanted to read input from console, obviously, declaring a function is not sufficient – it must be called to become effective:
#include <iostream>
void func()
{
std::cout << "Enter file name: ";
std::string fileName; std::getline(std::cin, fileName);
std::cout << "Got file name '" << fileName << "'\n");
}
int main ()
{
func();
return 0;
}
Output:
Enter file name: test.txt↵
Got file name 'test.txt'
Live Demo on coliru
C++ std vs. Qt
Qt is undoubtly built on top of the C++ std library. However, though it's possible it is not recommended to mix both APIs when it can be prevented (or there aren't specific reasons to do so).
Both, Qt and C++ std, are a possibility to write portable software.
Qt covers a lot of things which are provided in the std library as well but a lot of other things additionally which are not or not yet part of std. In some cases, the Qt is a bit less generic but more convenient though this is my personal opinion. IMHO, the following explains how I came to this:
std::string vs. QString
std::string stores a sequence of chars. The meaning of chars when exposed as glyph (e.g. printing on console or displaying in a window) depends on the encoding which is used in this exposing. There are lot of encodings which interprete the numbers in the chars in distinct ways.
Example:
std::string text = "\xc3\xbc";
Decoded/displayed with
Windows-1252: ü
UTF-8: ü
Based on character type of std::string, it is not possible to determine the encoding. Hence, an additional hint must be provided to decode this properly.
(AFAIK, it is similar for std::wstring and wchar_t.)
QString stores a sequence of Unicode characters. So, one universal encoding was chosen by design to mitigate the "encoding hell".
As long as the program operates on QString, no encoding issues should be expected. The same is true when combining QString with other functions of Qt. However, it becomes a bit more complicated when "leaving the Qt universe" – e.g. storing contents of a std::string to QString.
This is the point where the programmer has to provide the additional hint for the encoding of the contents in std::string. QString provides a lot of from...() and to...() methods which can be used to re-encode contents but the application programmer is still responsible to chose the right one.
Assuming that the intended contents of text should have been the ü (i.e. UTF-8 encoding), this can be converted to QString (and back) by:
// std::string (UTF-8) -> QString
std::string text = "\xc3\xbc";
QString qText = QString::fromUtf8(text.c_str());
// QString -> std::string (UTF-8)
std::string text2 = qText.toUtf8();
This has to be considered when input from std::cin shall be passed to QString:
std::cout << "Enter file name: ";
std::string input; std::getline(std::cin, input);
QString qFileName = QString::fromLocal8Bit(input);
And even now, the code contains a little flaw – the locale of std::cin might have changed with std::ios::imbue(). I must admit that I cannot say much more about this. (In daily work, I try to prevent this topic at all e.g. by not relying on Console input which I consider especially critical on Windows – the OS on which we usually deploy to customers.)
Instead, a last note about OP's code:
How to fix it
Remembering my above recommendation (not to mix std and Qt if not necessary), this can be done in Qt exclusively:
QTextStream qtin(stdin);
qtin.readline();
I must admit that I never did it by myself but found this in the Qt forum: Re: stdin reading.

Finding a specific character in a file in Qt

How can i find a specific character in a QFile which has a text in it?
for example i have ' $5000 ' written somewhere in my file. in want to find the "$" sign so i will realize that I've reached the number.
I tried using QString QTextStream::read(qint64 maxlen) by putting 1 as the maxlen :
QFile myfile("myfile.txt");
myfile.open(QIODevice::ReadWrite | QIODevice::Text);
QTextStream myfile_stream(&myfile);
while(! myfile_stream.atEnd())
{
if( myfile_stream.read(1) == '$')
{
qDebug()<<"found";
break;
}
}
and i get "error: invalid conversion from 'char' to 'const char* "
i also tried using the operator[] but apparently it can't be used for files.
Read in a line at a time and search the text that you've read in
QTextStream stream(&myFile);
QString line;
do
{
line = stream.readLine();
if(line.contains("$"))
{
qDebug()<<"found";
break;
}
} while (!line.isNull());
The error message you've posted doesn't match the issue in your code. Possibly the error was caused by something else.
QTextStream::read returns QString. You can't compare QString and const char* directly, but operator[] can help:
QString s = stream.read(1);
if (s.count() == 1) {
if (s[0] == '$') {
//...
}
}
However reading a file by too small pieces will be very slow. If your file is small enough, you can read it all at once:
QString s = stream.readAll();
int index = s.indexOf('$');
If your file is large, it's better to read file by small chunks (1024 bytes for example) and calculate the index of found character using indexOf result and count of already read chunks.
a single char could be read with
QTextStream myfile_stream(&myfile);
QChar c;
while (!myfile_stream.atEnd())
myfile_stream >> c;
if (c == '$') {
...
}
myfile_stream.read(1) - this is not good practice, you should not read from file one byte at a time. Either read the entire file, or buffered/line by line if there is a risk for the file to be too big to fit in memory.
The error you get is because you compare a QString for equality with a character literal - needless to say that is not going to work as expected. A string is a string even if there is only one character in it. As advised - use either the [] operator or better off for reading - QString::at() const which is guaranteed to create no extra copy. You don't use it on the QFile, nor on the QTextStream, but on the QString that is returned from the read() method of the text stream targeted at the file.
Once you have the text in memory, you can either use the regular QString methods like indexOf() to search for the index of a contained character.
in want to find the "$" sign so i will realize that I've reached the
number.
It sounds to me that you're searching for the '$' symbol because you're more interested in the dollar value that follows it. In this case, I suggest reading the files line by line and running them through a QRegExp to extract any values you're looking for.
QRegExp dollarFind("\\$(\\d+)");
while(!myfile_stream.atEnd()){
QString line = myfile_stream.readLine();
if (dollarFind.exactMatch(line)){
QStringList dollars = dollarFind.capturedTexts();
qDebug() << "Dollar values found: " << dollars.join(", ");
}
}

How to convert TBuf8 to QString

I've tried to convert using the following code:
template< unsigned int size >
static QString
TBuf82QString( const TBuf8< size > &buf )
{
return QString::fromUtf16(
reinterpret_cast<unsigned short*>(
const_cast<TUint8*>(
buf.Ptr() ) ), buf.Length() );
}
But It always returns something like ?????b.
EDIT: Changed code example
Using a template probably isn't a good solution, since it will result in a new instantiation of this block of code within your application binary, for every size of input string which is converted. Since the output type (QString) contains no compile-time constant, this means you end up with code bloat, for no gain.
A better approach would be to leverage the fact that TBuf8<N> inherits from TDesC8:
QString TBuf2QString(const TDesC8 &buf)
{
return QString::fromLocal8Bit(reinterpret_cast<const char *>(buf.Ptr()),
buf.Length());
}
TBuf<16> foo(_L("sometext"));
QString bar = TBuf2QString(foo);
TBuf8 is used for binary data or non-Unicode strings. TBuf16 is used for Unicode strings. TBuf is conditionally compiled and will always be TBuf16 as Symbian OS is natively Unicode.
Try using QString::fromLocal8Bit() with TBuf8::Ptr()

How to deal with "%1" in the argument of QString::arg()?

Everybody loves
QString("Put something here %1 and here %2")
.arg(replacement1)
.arg(replacement2);
but things get itchy as soon as you have the faintest chance that replacement1 actually contains %1 or even %2 anywhere. Then, the second QString::arg() will replace only the re-introduced %1 or both %2 occurrences. Anyway, you won't get the literal "%1" that you probably intended.
Is there any standard trick to overcome this?
If you need an example to play with, take this
#include <QCoreApplication>
#include <QDebug>
int main()
{
qDebug() << QString("%1-%2").arg("%1").arg("foo");
return 0;
}
This will output
"foo-%2"
instead of
"%1-foo"
as might be expected (not).
qDebug() << QString("%1-%2").arg("%2").arg("foo");
gives
"foo-foo"
and
qDebug() << QString("%1-%2").arg("%3").arg("foo");
gives
"%3-foo"
See the Qt docs about QString::arg():
QString str;
str = "%1 %2";
str.arg("%1f", "Hello"); // returns "%1f Hello"
Note that the arg() overload for multiple arguments only takes QString. In case not all the arguments are QStrings, you could change the order of the placeholders in the format string:
QString("1%1 2%2 3%3 4%4").arg(int1).arg(string2).arg(string3).arg(int4);
becomes
QString("1%1 2%3 3%4 4%2").arg(int1).arg(int4).arg(string2, string3);
That way, everything that is not a string is replaced first, and then all the strings are replaced at the same time.
You should try using
QString("%1-%2").arg("%2","foo");

Resources