How to use google-diff-match-patch library in C++ QT? - c++

I've downloaded google diff library for C++ Qt.
https://code.google.com/archive/p/google-diff-match-patch/
But I don't really understand how to use it for a simple comparing of two strings.
Let assume I have two QStrings.
QString str1="Stackoverflow"
QString str2="Stackrflow"
As I understood I need to create dmp object of diff_match_match class and then call the method for comparing.
So what do I do to have for example "ove has deleted from 5 position".

Usage is explained in the API wiki and diff_match_patch.h.
The position isn’t contained in the Diff object. To obtain it, you could iterate over the list and calculate the change position:
Unchanged substrings and deletes increment the position by the length of the unchanged/deleted substring.
Insertions do not alter positions in the original string.
Deletes followed by inserts are actually replacements. In that case the insert operation happens at the same position where the delete occured, so that last delete should not increment the position.
i.e. something like this (untested):
auto diffResult = diff_main(str1, str2);
int equalLength = 0;
int deleteLength = 0;
bool lastDeleteLength = 0; // for undoing position offset for replacements
for (const auto & diff : diffResult) {
if (diff.operation == Operation.EQUAL) {
equalLength += diff.text.length();
lastDeleteLength = 0;
}
else if (diff.operation == Operation.INSERT) {
pos = equalLength + deleteLength - lastDeleteLength;
qDebug() << diff.toString() << "at position" << pos;
lastDeleteLength = 0;
}
else if (diff.operation == Operation.DELETE) {
qDebug() << diff.toString() << "at position" << equalLength + deleteLength;
deleteLength += diff.text.length();
lastDeleteLength = diff.text.length();
}
}

Related

How to check if element exists in a linked list in c++?

The problem:
Imagine there is a linked list class which has multiple methods to use. The main part of the code is like this:
int main ()
{
List l;
l.push_back (86);
l.push_front (43);
l.push_front (12);
int intToSearchFor = 12;
if (l.exists (intToSearchFor))
{
cout << "(" << intToSearchFor << ") found :)";
}
else
{
cout << "(" << intToSearchFor << ") not found :(";
}
}
As you can see in this piece of code, the List class has two methods to prepend and append new items to the list. And also it has an iterator method which lets us loop over the items and check the data in each node.
The issue?
I want to create a method which check existence of an element. For example l.exists(12) should return either true or false.
My Solution:
Start to loop over the items with iterator.
Check if the item data is equal to the one you are looking for.
If it is equal then return true. Otherwise if there are more items in the list, move into the next item in the list and go to step 2.
If there are no more items in the list return false.
bool List::exists (int x)
{
Iterator it = this->get_iterator ();
do {
if (it.current->data == x) {
return true;
}
it.current = it.current->next;
} while (it.has_more_elements ());
return false;
}
Full answer:
http://cpp.sh/6cfdh
You should check whether a pointer points to nullptr before accessing data pointed by the pointer (see has_more_elements()). Better naming may avoid some confusion.
while(it.current->data!=x && it.has_more_elements())
it.current=it.current->next;
return (it.current->data==x)?true:false;
If x is not present and it reaches end of list, it.current->data will cause run time error as it may be NULL.
while(it.has_more_elements() && it.current->data!=x)
it.current = it.current->next;
return it.current!=NULL;

iterating vector of strings C++

The code is to read instructions from text file and print out graphic patterns. One is my function is not working properly. The function is to read the vectors of strings I've got from the file into structs.
Below is my output, and my second, third, and sixth graphs are wrong. It seems like the 2nd and 3rd vectors are not putting the correct row and column numbers; and the last one skipped "e" in the alphabetical order.
I tried to debug many times and still can't find the problem.
typedef struct Pattern{
int rowNum;
int colNum;
char token;
bool isTriangular;
bool isOuter;
}Pattern;
void CommandProcessing(vector<string>& , Pattern& );
int main()
{
for (int i = 0; i < command.size(); i++)
{
Pattern characters;
CommandProcessing(command[i], characters);
}
system("pause");
return 0;
}
void CommandProcessing(vector<string>& c1, Pattern& a1)
{
reverse(c1.begin(), c1.end());
string str=" ";
for (int j = 0; j < c1.size(); j++)
{
bool foundAlpha = find(c1.begin(), c1.end(), "alphabetical") != c1.end();
bool foundAll = find(c1.begin(), c1.end(), "all") != c1.end();
a1.isTriangular = find(c1.begin(), c1.end(), "triangular") != c1.end() ? true : false;
a1.isOuter = find(c1.begin(), c1.end(), "outer") != c1.end() ? true : false;
if (foundAlpha ==false && foundAll == false){
a1.token = '*';
}
//if (c1[0] == "go"){
else if (c1[j] == "rows"){
str = c1[++j];
a1.rowNum = atoi(str.c_str());
j--;
}
else if (c1[j] == "columns"){
str = c1[++j];
a1.colNum = atoi(str.c_str());
j--;
}
else if (c1[j] == "alphabetical")
a1.token = 0;
else if (c1[j] == "all"){
str = c1[--j];
a1.token = *str.c_str();
j++;
}
}
}
Before debugging (or posting) your code, you should try to make it cleaner. It contains many strange / unnecessary parts, making your code harder to understand (and resulting in the buggy behaviour you just described).
For example, you have an if in the beginning:
if (foundAlpha ==false && foundAll == false){
If there is no alpha and all command, this will be always true, for the entire length of your loop, and the other commands are all placed in else if statements. They won't be executed.
Because of this, in your second and third example, no commands will be read, except the isTriangular and isOuter flags.
Instead of a mixed structure like this, consider the following changes:
add a default constructor to your Pattern struct, initializing its members. For example if you initialize token to *, you can remove that if, and even the two bool variables required for it.
Do the parsing in one way, consistently - the easiest would be moving your triangular and outer bool to the same if structure as the others. (or if you really want to keep this find lookup, move them before the for loop - you only have to set them once!)
Do not modify your loop variable ever, it's an error magnet! Okay, there are some rare exceptions for this rule, but this is not one of them.
Instead of str = c1[++j];, and decrementing later, you could just write str = c1[j+1]
Also, are you sure you need that reverse? That makes your relative +/-1 indexing unclear. For example, the c1[j+1 is j-1 in the original command string.
About the last one: that's probably a bug in your outer printing code, which you didn't post.

Pointer and QVector issue

I want to define functions which will delete self-defined type object and the index from the QVector.
Originally the source was as follows:
Point PointCollection::RemovePoint(int index)
{
Point removedPoint = new Point(this[index].Id, this[index].X, this[index].Y);
this->remove(index);
updateCentroid();
return (removedPoint);
}
Point PointCollection::RemovePoint(Point p)
{
Point removedPoint = new Point(p.GetId(), p.GetX(), p.GetY());
this.remove(p);
updateCentroid();
return (removedPoint);
}
which was not working as I think because of new. Then I modified the source to the following:
Point PointCollection::deletePoint(int Index)
{
Point deleted = Point(this[Index].Id, this[Index].X, this[Index].Y);
this->remove(Index);
updateCentroid();
return(deleted);
}
Point PointCollection::deletePoint(Point point)
{
Point deleted = Point(point.GetId(), point.GetX(), point.GetY());
this->remove(point);
updateCentroid();
return(deleted);
}
Now Point PointCollection::deletePoint(int Index) compiles without any error, but this->remove(point); in Point PointCollection::deletePoint(Point point) functioned is compiled with following error:
error: no matching function for call to 'PointCollection::remove(Point&)'
Q1: Did I do correct that removed new?
Q2: How to fix the error I am having.
Your approach seems to be overall wrong. First of all focus on what you need:
performance and memory efficiency or...
fast inserts and deletes
QVector is of the former kind. If you do deletes and inserts that are not in the back you will likely get poor performance. Because the entire vector has to be reallocated every time you do a change.
If you need to insert and delete often go for a linked list, for example QLinkedList.
Qt already provides containers, implementing your own doesn't offer much benefit, it is not likely that you will produce a better container than a bunch of professionals working on this framework for 20 years.
Here is a simple snippet how to insert and delete points in vector and linked list. You can use this methods to implement your own wrapper class if you want:
QVector<QPoint> myPointVector;
QLinkedList<QPoint> myPointList;
// push back some data
myPointVector << QPoint(1, 1) << QPoint(2, 2) << QPoint(3, 3) << QPoint(4, 4);
myPointList << QPoint(1, 1) << QPoint(2, 2) << QPoint(3, 3) << QPoint(4, 4);
foreach (QPoint p, myPointVector) qDebug() << p;
foreach (QPoint p, myPointList) qDebug() << p;
qDebug() << endl;
auto i1 = myPointVector.indexOf(QPoint(2, 2));
auto i2 = qFind(myPointList.begin(), myPointList.end(), QPoint(2,2));
myPointVector.insert(i1, QPoint(5,5)); // or existing point object / reference
auto i3 = myPointList.insert(i2, QPoint(5,5));
foreach (QPoint p, myPointVector) qDebug() << p;
foreach (QPoint p, myPointList) qDebug() << p;
qDebug() << endl;
QPoint deletedFromVector = myPointVector[i1]; // use those to return before deleting
QPoint deletedFromList = *i3; // note you don't need to construct just assign
myPointVector.remove(i1);
myPointList.erase(i3);
foreach (QPoint p, myPointVector) qDebug() << p;
foreach (QPoint p, myPointList) qDebug() << p;
qDebug() << endl;
As you see, initially both containers contain points 1 2 3 4, then point 5 is inserted in the place of 2, then removed again. The vector uses an integer index for the operations, the list uses an iterator. That is why when 5 is inserted, I get its "index" because unlike the vector it won't push back the rest, so if i2 is removed it will not remove point 5 which was inserted in place of point 2, but point 2 to which it still refers to.
Also, if you want to insert in the list in a given index, you can just use the begin iterator + index to "forward" the iterator the appropriate number of positions.
Hopefully that made sense. Naturally you can use you point class in the place of QPoint.

How to use pqxx::stateless_cursor class from libpqxx?

I'm learning libpqxx, the C++ API to PostgreSQL. I'd like to use the pqxx::stateless_cursor class, but 1) I find the Doxygen output unhelpful in this case, and 2) the pqxx.org website has been down for some time now.
Anyone know how to use it?
I believe this is how I construct one:
pqxx::stateless_cursor <pqxx::cursor_base::read_only, pqxx::cursor_base::owned>
cursor( work, "SELECT * FROM mytable", ?, ? );
The last two parms are called cname and hold, but are not documented.
And once the cursor is created, how would I go about using it in a for() loop to get each row, one at a time?
Thanks #Eelke for the comments on cname and hold.
I figured out how to make pqxx::stateless_cursor work. I have no idea if there is a cleaner or more obvious way but here is an example:
pqxx::work work( conn );
pqxx::stateless_cursor<pqxx::cursor_base::read_only, pqxx::cursor_base::owned>
cursor( work, "SELECT * FROM mytable", "mycursor", false );
for ( size_t idx = 0; true; idx ++ )
{
pqxx::result result = cursor.retrieve( idx, idx + 1 );
if ( result.empty() )
{
// nothing left to read
break;
}
// Do something with "result" which contains a single
// row in this example since we told the cursor to
// retrieve row #idx (inclusive) to idx+1 (exclusive).
std::cout << result[ 0 ][ "name" ].as<std::string>() << std::endl;
}
I do not know the pqxx library but based on the underlying DECLARE command of postgresql I would guess
That cname is the name of the cursor, so it can be anything postgresql normally accepts as a cursor name.
That hold refers to the WITH HOLD option of a cursor, from the docs:
WITH HOLD specifies that the cursor can continue to be used after the
transaction that created it successfully commits. WITHOUT HOLD
specifies that the cursor cannot be used outside of the transaction
that created it. If neither WITHOUT HOLD nor WITH HOLD is specified,
WITHOUT HOLD is the default.
Here's another cursor example, using a do-while() loop:
const std::conStr("user=" + opt::dbUser + " password=" + opt::dbPasswd + " host=" + opt::dbHost + " dbname=" + opt::dbName);
pqxx::connection conn(connStr);
pqxx::work txn(conn);
std::string selectString = "SELECT id, name FROM table_name WHERE condition";
pqxx::stateless_cursor<pqxx::cursor_base::read_only, pqxx::cursor_base::owned>
cursor(txn, selectString, "myCursor", false);
//cursor variables
size_t idx = 0; //starting location
size_t step = 10000; //number of rows for each chunk
pqxx::result result;
do{
//get next cursor chunk and update the index
result = cursor.retrieve( idx, idx + step );
idx += step;
size_t records = result.size();
cout << idx << ": records pulled = " << records << endl;
for( pqxx::result::const_iterator row : result ){
//iterate over cursor rows
}
}
while( result.size() == step ); //if the result.size() != step, we're on our last loop
cout << "Done!" << endl;

STL Vector Erase Not Working

Briefly, I am trying to write a routine that reads comma separated values from a file into a stl vector. This works fine. The values in the csv file might also be in double quotes so I have handled this too by trimming them. However, there is one problem where the values between the quotes might also have commas in them which are not to be treated as delimiters.
If I have a file containing the line
"test1","te,st2","test3","test4"
My file routine reads this into a vector as
"test1"
"te
st2"
"test3"
"test4"
I wrote a routine which I just called PostProcessing. This would go through the vector and correct this problem. It would take each element and check of the first value was a quote. If so it would remove it. It would then look for another quote at the end of the string. If it found one it would just remove it and move onto the next item. If it didn't find one, it would keep going through the vector merging all the following items together until it did find the next quote.
However, this works in merging "te and st2" together into element 2 (index 1) but when I try and erase the required element from the vector it must be failing as the resulting vector output is as follows:
test1
test2
st2"
test3
"test4"
Note also the last element has not been processed because I derement the size of the count but as the vector erase has failed the true count hasn't actually changed.
The PostProcessing code is below. What am I doing wrong?
bool MyClass::PostProcessing()
{
bool bRet = false;
int nCount = m_vecFields.size();
for (int x = 0; x < nCount; x++)
{
string sTemp = m_vecFields[x];
if (sTemp[0] == '"')
{
sTemp.erase(0,1);
if (sTemp[sTemp.size()-1] == '"')
{
sTemp.erase(sTemp.size()-1, 1);
m_vecFields[x] = sTemp;
}
else
{
// find next double quote and merge these vector items
int offset = 1;
bool bFound = false;
while (x+offset < nCount && !bFound)
{
sTemp = sTemp + m_vecFields[x+offset];
if (sTemp[sTemp.size()-1] == '"')
{
// found corresponding "
sTemp.erase(sTemp.size()-1,1);
bFound = true;
}
else
{
offset++;
}
}
if (bFound)
{
m_vecFields[x] = sTemp;
// now remove required items from vector
m_vecFields.erase(m_vecFields.begin()+x+1, m_vecFields.begin()+x+offset);
nCount -= offset;
}
}
}
}
return bRet;
}
Edit: I've spotted a couple of issues with the code which I will be correcting but they don't affect the question being asked.
m_vecFields.erase(m_vecFields.begin()+x+1, m_vecFields.begin()+x+offset);
This function takes a semi-closed interval, which means the "end" of the interval to erase should point one-past the last element to erase. In your case, it points to that element. Change the second argument to m_vecFields.begin()+x+offset+1.
x += offset;
Since you've just processed an item and deleted everything up to the next item, you shouldn't skip offset items here. The x++ from the loop will do just fine.