Continue a while loop until EOF (Qt) - c++

I'm working on a program to create a list from a large QStringList. So basically after a string match, a while loop will start to add the next strings into a list. This part is working fine. The only problem I have is that the program quits unexpectedly because I don't know how I should add the EOF mechanism.
Update with a more detailed code
Sorry for not providing with enough details about my code. This is how my code looks now. So after the first time the string "PACKAGE TYPE" is detected, I use the function storeLines() function to store the next strings into one of three lists dependent. And this will continue until the next "PACKAGE TYPE" match or EOF. The only thing that is not working correctly now is when the iterator is on the last string of the QStringList. It somehow doesn't detect that the next is inputline.end()
void storeLines(QString department, QStringList::iterator current_line, QStringList::iterator endline){
while(QString::compare(*(current_line + 1),"PACKAGE TYPE") && (++current_line != endline)){ //this is not working
if(!QString::compare(department, "MDA")) mda_list.push_back(*current_line);
else if(!QString::compare(department, "SDA")) sda_list.push_back(*current_line);
else mix_list.push_back(*current_line);
}
}
void void MainWindow::on_pushButton_clicked(){
QString input = ui->listinput->toPlainText().toLatin1();
QStringList inputline = input.split("\n", QString::SkipEmptyParts );
for(QStringList::iterator pkg_header(inputline.begin()); pkg_header != inputline.end(); ++pkg_header){
if(!QString::compare(*pkg_header,"PACKAGE TYPE")){
++pkg_header;
if(!QString::compare(*pkg_header,"Department-mda:")) storeLines("MDA", pkg_header, inputline.end());
else if(!QString::compare(*pkg_header,"Department-sda:")) storeLines("SDA", pkg_header, inputline.end());
else storeLines("MIX", pkg_header, inputline.end());
}
}
}
Thanks in advance!

You're writing a parser - it's often simplest to write it like you'd usually write one, by making the states explicit and iterating in sequence over every element of the input stream. You won't make any off-by-one iterator errors that way.
This code matches the intent in your question, and makes it obvious that you've missed a case: when you expect a department, you don't react to the PACKAGE TYPE being present. You could signal an error, or stay in the DEPARTMENT state, but I presume you should handle it and not just ignore it.
QStringList mda_list, sda_list, mix_list;
void parse(const QString & input) {
enum {
TYPE,
DEPARTMENT,
ITEMS
} state = TYPE;
auto list = &mix_list;
auto const kPackageType = QStringLiteral("PACKAGE TYPE");
for (auto const element : input.split("\n", QString::SkipEmptyParts)) {
switch (state) {
case TYPE:
if (element == kPackageType)
state = DEPARTMENT;
break;
case DEPARTMENT:
if (element == QStringLiteral("Department-mda:"))
list = &mda_list;
else if (element == QStringLiteral("Department-sda:"))
list = &sda_list;
state = ITEMS;
break;
case ITEMS:
if (element == kPackageType)
state = DEPARTMENT;
else
*list << element;
break;
}
}
}
The use of QStringLiteral gives you compile-time-built string instances to compare against. The code would work about just as well if you removed the QStringLiteral(...) wrappers, at a cost due to inevitable premature pessimization though.

Your apparent intent is to find the string "PACKAGE TYPE" in the existing list, and then copy the remaining elements in the list to a new list.
If that's the case, then why not do exactly that?
for(QStringList::iterator current_line(inputline.begin()); current_line != inputline.end(); ++current_line){
if(!QString::compare(*current_line,"PACKAGE TYPE"){
list.insert(list.end(), ++current_line, inputline.end());
break;
}
}

Related

Bool function to compare two list of structs

This bit of code below is reading in a stringstream and adding each piece to a struct, and once its done its adds the whole struct to a list of structs.board is the temporary struct and then
while (getline(ss, word, ',')){
if (wordIndex==0){
board.item = word;
}
else if (wordIndex==1&&word==" for sale"){
board.forSale = false;
}
else if (wordIndex==1&&word==" wanted"){
board.forSale = true;
}
else if (wordIndex==2){
board.price = atoi(word.c_str());
}
wordIndex++;
}
index ++;
messageBoard.push_back(board);
I also have this function im trying to create that compares the contents of board, with the contents of array. Not sure if I need to compare it with an iterator or just compare the items individually.
bool compare(struct messageBoard* one, struct messageBoard* two){
return(one.item==two.item&&one.forSale!=two.forSale&&one.price<=two.price)
}
Thats as far as I've gotten. The goal is if Item matches and the forSale values are opposites(True vs False) and the price of the for sale is <= to the wanted item, then the bool function returns True, else it returns false. I want to run it right before the
arr.push_back(board);

"String Iterators Incompatible" error message when running bubble sort program

I am a beginner trying to bubble sort a vector of objects in C++. My goal is to sort the vector by member variables of each object element's member variable. So in the end, I would like the attributes off all the vector elements to be the same, just sorted in a different order. When I run the program, I get the following message:
Here is my code:
void sortInventory(vector<Vehicle> &carList)
{
bool swap;
Vehicle temp;
do
{
swap = false;
for (int count = 0; count < carList.size(); count++)
{
transform(carList[count].getVIN().begin(), carList[count].getVIN().end(), carList[count].getVIN().begin(), ::tolower);
if (carList[count].getVIN() > carList[count + 1].getVIN())
{
temp = carList[count];
carList[count] = carList[count + 1];
carList[count + 1] = temp;
swap = true;
}
}
} while (swap);
}
Here is my class declaration:
class Vehicle
{
private:
string VIN;
public:
string getVIN();
void setVIN(string);
};
Here is my class implementation:
string Vehicle::getVIN()
{ return VIN; }
void Vehicle::setVIN(string input)
{ VIN = input; }
By the way, I am aware that I am not using efficient methods, but I am just starting to learn the language and I am learning to write the code.
I asked a question similar to this here. However, none of the answers got me to where I wanted to go, although I feel like I am going in the right direction.
This line of code attempts to convert the string for the VIN into lowercase text, but fails:
transform(carList[count].getVIN().begin(),
carList[count].getVIN().end(),
carList[count].getVIN().begin(),
::tolower);
Each call to getVIN() results in a separate string instance. Since the iterators are not from the same string instance, the failure is the result.
You don't show how you populate your carList, but one possible way to fix this is to save the VIN in lowercase at the time you save the VIN in the carList.
As jxh says, your transform line fails because you are making iterators to separate string objects. Why not try making the transform a separate routine?
If you want to be fancy you can define it inside the sort routine as a lambda function. Or you can just make it a separate routine defined separately.
// returns a lower case version of the string
std::string lower_case(std::string VIN_number){
auto begin = std::begin(VIN_number);
auto end = std::end(VIN_number);
// Your code acting on one fixed string
std::transform(begin, end, begin, ::tolower);
return VIN_number;
}
Then when you do your comparison, do something like
if ( lower_case(carList[count].getVIN()) > lower_case(carList[count + 1]).getVIN()) )

Pointers/C-Strings in C++. How to filter the strings?

I have an array to filter.
Example:
str = "hellothere" and filter = "eo". What to do when i need to filter?
void filter_str(char* str, char* filter, char*result)
{
while(*str)
{
if() //If the current character in str is one to be filter.
{
*str++;
}
else
{
*result++ = *str++;
}
}
*result = '\0';
}
I just can't figure out how to check if the current character is one that needs to be filter. Since the filter can be more than one character, such as "eo". How do I check both "e" and "o" every loop, and then reset filter back to "e" at the start.
I wanted to make a pointer to the start of filter, then use that at the end of the while to go back to the start of filter. But I am not sure how to make it check *str against all the characters to be filtered.
In this case a function has already been written that will do the hard work for you
if (strchr(filter, *str))
In general this is the answer to any question where you have processing that is too complex for you to handle. Write a function to solve the 'inner' problem, and then use that function in the 'outer' problem. In this case the inner problem is finding a character in a string, and the outer problem is the filtering operation you are doing. You are just lucky that the inner problem has already been solved for you.
If you want to know if a letter is in filter, the "easy" way (without using STL functions) would be to loop through the elements of filter and check each one of them to see if you find the character you are looking for.
while(*str)
{
bool found = false;
// For each element in filter {
// If *str == element {
found = true;
break; // This function gets you out of the 'for' loop
}
}
if(found) //If the current character in str is one to be filter.
{
You can fill up the pseudocode in comments

How are recursive backtracking returns handled with the void type

To generalize this question I am borrowing material from a Zelenski CS class handout. And, it is relevant to my specific question since I took the class from a different instructor several years ago and learned this approach to C++. The handout is here. My understanding of C++ is low since I use it occasionally. Basically, the few times I have needed to write a program I return to the class material, found something similar and started from there.
In this example (page 4) Julie is looking for a word using a recursive algorithm in a string function. To reduce the number of recursive calls she added a decision point bool containsWord().
string FindWord(string soFar, string rest, Lexicon &lex)
{
if (rest.empty()) {
return (lex.containsWord(soFar)? soFar : "");
} else {
for (int i = 0; i < rest.length(); i++) {
string remain = rest.substr(0, i) + rest.substr(i+1);
string found = FindWord(soFar + rest[i], remain, lex);
if (!found.empty()) return found;
}
}
return ""; // empty string indicates failure
}
To add flexibility to how this algorithm is used, can this be implemented as a void type?
void FindWord(string soFar, string rest, Lexicon &lex, Set::StructT &words)
{
if (rest.empty()) {
if (lex.containsWord(soFar)) //this is a bool
updateSet(soFar, words); //add soFar to referenced Set struct tree
} else {
for (int i = 0; i < rest.length(); i++) {
string remain = rest.substr(0, i) + rest.substr(i+1);
return FindWord(soFar + rest[i], remain, lex, words); //<-this is where I am confused conceptually
}
}
return; // indicates failure
}
And, how about without the returns
void FindWord(string soFar, string rest, Lexicon &lex, Set::StructT &words)
{
if (rest.empty()) {
if (lex.containsWord(soFar))
updateSet(soFar, words); //add soFar to Set memory tree
} else {
for (int i = 0; i < rest.length(); i++) {
string remain = rest.substr(0, i) + rest.substr(i+1);
FindWord(soFar + rest[i], remain, lex, words); //<-this is where I am confused conceptually
}
}
}
The first code fragment will try all permutations of rest, appended to the initial value of soFar (probably an empty string?). It will stop on the first word found that is in lex. That word will be returned immediately as it is found, and the search will be cut short at that point. If none were in lex, empty string will be returned eventually, when all the for loops have ran their course to the end.
The second fragment will only try one word: the concatenation of initial soFar and rest strings. If that concatenated string is in lex, it will call updateSet with it. Then it will return, indicating failure. No further search will be performed, because the return from inside the for loop is unconditional.
So these two functions are completely different. To make the second code behave like the first, you need it to return something else to indicate a success, and only return from within the for loop when FindWord call return value indicates a success. Obviously, void can not be used to signal failure and success. At the very least, you need to return bool value for that.
And without the returns your third code will perform an exhaustive search. Every possible permutation of initial string value of rest will be tried for, to find in the lexicon.
You can visualize what's going on like this:
FindWord: soFar="" rest=...........
for: i=... rest[i]=a
call findWord
FindWord: soFar=a rest=..........
for: i=... rest[i]=b
call findWord
FindWord: soFar=ab rest=.........
for: i=... rest[i]=c
call findWord
if return, the loop will be cut short
if not, the loop continues and next i will be tried
......
FindWord: soFar=abcdefgh... rest=z
for: i=0 rest[0]=z
call findWord
FindWord: soFar=abcdefgh...z rest="" // base case
// for: i=N/A rest[i]=N/A
if soFar is_in lex // base case
then do_some and return soFar OR success
else return "" OR failure
Each time the base case is reached (rest is empty) we have n+1 FindWord call frames on the stack, for n letters in the initial rest string.
Each time we hit the bottom, we've picked all the letters from rest. The check is performed to see whether it's in lex, and control returns back one level up.
So if there are no returns, each for loop will run to its end. If the return is unconditional, only one permutation will be tried - the trivial one. But if the return is conditional, the whole thing will stop only on first success.

logic issue or something more?

My program simulates a video store. In my list there are multiple copies of some videos. If I try to rent a video and the first copy of that video in the list is already rented, my program fails to continue checking to see if the other copies are available (a film is available if custId is '0000'). Take a look at the text file from where the list gets its members for a better understanding of what i'm describing:
Could anyone take a look and let me know if they spot an issue? Any help is appreciated, thanks.
Code from main
try
{
int index = 0;
bool found = false;
while (!found)
{
if (strncmp(filmId,filmList.getAt(index).number,6) == 0 && strncmp("0000",filmList.getAt(index).rent_id,5) == 0)//If that film is rented by NO customer
{
found = true;//customer can rent it
strcpy(newItem.number,filmId);//copy filmId into newItem
filmList.retrieve(newItem);//copy the struct in our orderedList with the same filmId/copy into newItem
filmList.remove(newItem);//delete the struct with same filmId/copy as newItem from the orderedList
strcpy(newItem.rent_id,custId);//update info in
strcpy(newItem.rent_date,rentDate);// newItem to show
strcpy(newItem.return_date,dueDate);// that it has been rented
filmList.insert(newItem);//put NewItem into list, effectivily replacing the removed item.
cout << "Rent confirmed!" << endl;
}
else
{
if (strncmp(filmId,filmList.getAt(index).number,6) > 0 || strncmp("0000",filmList.getAt(index).rent_id,5) > 0)
{
++ index;
}
else
{
throw string ("Not in list");
}
}
}
}
catch (string s)
{
cout << "\n***Failure*** " << s << endl;
}
Let me know if more code is required from any other parts of the program.
Here's my best guess with the code provided.
Let's say we are looking up 101001Casablanca, therefore I'm assuming filmId = "101001Casablanca". Also, assume the 101001Casablanca is checked out to customer 0001. We are comparing the first 6 characters of filmId to filmList.getAt(index).number, which I'm going to assume is at the very least "101001". This passes, but since it is checked out the second condition fails.
In the else we check the same strings in the first condition and still get 0 returned from strncmp which is false. The second condition is also false since strncmp("0000", "0001", 5) is -1. Therefore we go to the final else which throws.
If you are only checking string equality with strncmp, remember that it can return -1, therefore check if equal or not equal to 0.