I will start by saying I am reasonably new as a C++ programmer.
However I understand PHP and VBA, so have a good understanding of the aspects of programming fundamentals.
Because I use CSV's quite often in my day to day job, I thought it would be a good learning exercise to write a library that manipulates CSV Files.
I wrote this function:
int getHeaders(ifstream & os, vector<string> & head2){
string STRING;
getline(os,STRING);
cout << STRING << endl;
STRING.erase(remove(STRING.begin(), STRING.end(), '\"'), STRING.end());
string::iterator it = STRING.begin();
int x = 0;
for (int index = 0; it < STRING.end(); it++, index++) {
if (*it == ',') {
head2.push_back(STRING.substr(0,index));
STRING.erase(0,index+1);
cout << endl << head2[x];
cout << endl << STRING;
x++;
}
}
return head2.size();
}
Which is called by the following:
int addRowCount = 0;
vector<string> head1;
ifstream outfile;
outfile.open("default.csv", ios_base::app);
cout << getHeaders(outfile, head1) << endl;
cout << head1[0] << endl << head1[1] << endl;
But when I run the program, the program just dumps a load of random rubbish to the console (and crashes the application)
I am using windows so cannot use valgrind.
Does anyone know why this may be happening? Obviously this "dump" is not what I want the application to do. I am hoping someone can point out the part of my code which would make this happen.
Thanks in advance.
When you call erase on a string, iterators into that string are invalidated, so it is an error to use it after the call to STRING.erase().
Hint: When you look at the documentation for a method on a class that supports iterators, keep an eye out for notes about invalidating iterators. On this page, for example, read the section titled Iterator validity
[not related to the answer, but a style issue: Using ALL CAPS for a variable name like STRING is generally considered bad style in C and C++. All caps names are used for #defined symbols]
So it turns out that head2[1] wasn't set so it was some sort of memory leak.
Here is the finished function after a few further amendments for the advice in the comments section:
int getHeaders(ifstream & os, vector<string> & head2){
string STRING;
getline(os,STRING);
STRING.erase(remove(STRING.begin(), STRING.end(), '\"'), STRING.end());
int strle = count(STRING.begin(),STRING.end(), ',') + 1;
for(int x = 0; x != strle; x++){
if (count(STRING.begin(), STRING.end(), ',') > 0) {
head2.push_back(STRING.substr(0,STRING.find_first_of(',')));
} else {
head2.push_back(STRING.substr(0,STRING.length()));
}
STRING.erase(0,STRING.find_first_of(',')+1);
}
return head2.size();
}
Related
So while working through a course on Udemy over C++ one of the challenges was to check a string to see whether it was a palindrome or not. I completed the task successfully but went about it a different way than the instructor. I understand there are a multitude of ways to complete a task but I am wondering which is more efficient and why? It may seem stupid to be wondering about this while reteaching myself coding but I feel this is something I should be keeping in mind.
//Instructors code//
# include<iostream>
using namespace std;
/*program for reverse a string and check a string is a palidrome
*/
int main()
{
string str="MADAM";
string rev="";
int len=(int)str.length();
rev.resize(len);
for(int i=0, j=len-1; i<len; i++, j--)
{
rev[i]=str[j];
}
rev[len]='\0';
if(str.compare(rev)==0)
cout<<"palindrome"<<endl;
else
cout<<"not a pallindrome"<<endl;
return 0;
}
My Approach
#include <iostream>
using namespace std;
int main(){
string str1="test";
// cout << "Enter a string to check if it is a Palindrome: ";
// getline(cin,str1);
string str2;
string::reverse_iterator it;
for(it=str1.rbegin(); it!= str1.rend(); it++)
{
str2.push_back(*it);
}
if(!str1.compare(str2))
cout << "\nPalindrome";
else
cout << "\nNot a Palindrome";
return 0;
}
Thank you in advance.
In theory the code from your instructor is more efficient, but both examples have issues.
With your instructors code the main issue is the use of
int len=(int)str.length();
In this example, it is okay because we know the size of the string will fit in a int, but if you were getting a string from an outside source, this could be a problem. A std::string using an unsigned integer type to store the size of the string and that means you can have a string who's size is larger then what can fit in an int. If that were to happen, then code is not going to work correctly.
With your code you a avoid all that, which is great, but you also leave some performance on the table. In theory your code of
for(it=str1.rbegin(); it!= str1.rend(); it++)
{
str2.push_back(*it);
}
is going to cause str2 to have multiple buffer allocations and copies from the old buffer to the new buffer as it grows. This is a lot of extra work that you don't need to do since you already know how much space you need to allocate. Having
str2.reserve(str1.size() + 1);
before the loop pre-allocates all the space you need so you don't have those potential performance hits.
Then we come to the fact that both of your examples are using a second string. You don't need another string to check for a palindrome. What you can do is just check and see if the first and last characters are the same, and if they are move on to the first+1 and last-1 character and so on until you reach the middle or they don't match. You can do that using a construct like
bool is_palindrome = true;
for (auto start = str.begin(), end = str.end() - 1;
start < end && is_palindrome;
++start, --end)
{
if (*start != *end)
is_palindrom = false
}
if (is_palindrome)
std::cout << "palindrome\n";
else
std::cout << "not a pallindrome\n";
The simplest and most efficient way (no copying required) would be something like this:
inline bool is_palindrome(const std::string& u) {
return std::equal(u.begin(), std::next(u.begin(), u.length() / 2), u.rbegin());
}
I would say that both are almost the same, but as mentioned in the comments, the line:
str2.push_back(*it);
Is actually very inefficient, since std::string may copy the existing string to a new location in the memory, and then append the next char to the string, which is wasteful.
But I am wondering, why to create the copy in the first place?
It is very simple to run both from start to end, and from end to start to check it out, meaning:
bool is_polindrom(const std::string& str)
{
for (std::size_t idx = 0, len = str.length(); idx < len / 2; ++idx)
{
if (str[idx] != str[len - 1 - idx])
{
return false;
}
}
return true;
}
Running the code with:
int main()
{
const std::string right1 = "MADAM";
const std::string right2 = "MAAM";
const std::string wrong1 = "MADAAM";
const std::string wrong2 = "MEDAM";
std::cout << "MADAM result is: " << is_polindrom(right1) << std::endl;
std::cout << "MAAM result is: " << is_polindrom(right2) << std::endl;
std::cout << "MADAAM result is: " << is_polindrom(wrong1) << std::endl;
std::cout << "MEDAM result is: " << is_polindrom(wrong2) << std::endl;
}
Will yield:
MADAM result is: 1
MAAM result is: 1
MADAAM result is: 0
MEDAM result is: 0
You don't need extra memory in this case, since it is possible to iterate over a string from the end to the beginning, and you need to run on it exactly once (and notice that I stop when idx >= len / 2 since you don't really need to check each letter twice!).
myclass::myclass(queue<queue<char> > construction_info){
//why does this line crash?
queue<char> first_line_of_construction_info = construction_info.front();
construction_info.pop();
}
I am reading from text files (not generated by me so I can't change the format), into a queue of queue of char. It means lines of characters. And I process that info to generate the class. However, after working in a few debug messages I realized that the first time I am getting a bad_alloc on execute (the program initialized all myclasses from text files at startup) is this line in the code.
I'm new to working with C++ and my google-fu hasn't really helped me with this problem. Does anyone have any suggestions as to where I can start solve this crash?
Simply uncommenting the class constructor is letting my program work without any crashes, obviously without generating actually useful objects of course.
Using g++ with c++11 on linux.
Edit:
Here is the full code cut from the main file:
int initialize_classrooms(){
path p = "files/classrooms/";
//files of lines of queues of chars
//vector of vector of queue of char
vector<queue<queue<char> > > classroom_files;
if(exists(p)){
for (directory_entry& x : directory_iterator(p)){
queue<queue<char> > cur_file;
ifstream file(x.path().filename().string());
queue<char> cur_line;
char ch;
while (file >> noskipws >> ch) {
if(!isspace(ch)){
cur_line.push(ch);
}else if(ch == '\n'){
cur_file.push(cur_line);
cur_line = queue<char>();
}
}
classroom_files.push_back(cur_file);
cur_file = queue<queue<char> >();
file.close();
}
}else{
cout << "Classroom files are missing!" << endl;
return 1;
}
cout << "Got all the way to classroom creation" << endl;
int i = 1;
for(auto cf : classroom_files){
cout << "Number of loops: " << i << endl;
i++;
shared_ptr<classroom> cr = shared_ptr<classroom>(new classroom(cf));
}
cout << "Got past the classroom creation" << endl;
return 0;
}
If the goal is only to read the contents (of the queue at the front), then creating a (constant) reference is preferred.
queue<char> const& first_line_of_construction_info = construction_info.front();
^^^^^^
After "read"ing, it can be poped just as in current code.
EDIT: (Thanks to #Remy Lebeau)
Since copies are wastefule, myclass constructor can take construction_info by reference instead of by value.
myclass::myclass(queue<queue<char> > const& construction_info) {
^^^^^^
Look in the rest of your code too, you probably do not want multiple copies of these queue-of-queues floating around.
Aside: Unless otherwise constrained, instead of using a queue<char> for storing a line of text, consider using std::string.
I am looking for some quick tips on a homework assignment. We are given a few problems and have to write two quick programs on how to solve the problems with one each of iteration and recursion. I'm sure this is easier than I think, but I am getting easily confused over the two. By no means do I want anyone to fully solve the problems for me, I won't learn anything! But if you could look at what I have so far and let me know if I am heading in the right direction. Also, the code does not need to compile, our professor wants us to have a general idea of the differences of iteration vs. recursion.
Problem: check a string to see if it is a palindrome.
My solution- I think it is the iterative solution:
bool iterative_palindrome (const string& str) {
string line, result;
stack <char> stack_input;
//user enters string, program takes it
cout << "Enter string: " << endl;
while (getline (cin, line) && (line != "")) {
//push string into stack
for (size_t i = 0; i < line.size(); i++) {
stack_input.push(line[i]);
//create reverse of original string
while (!stack_input.empty()) {
result += stack_input.top();
stack_input.pop();
return result;
}
//check for palindrome, empty string
if (line == result || line = "0" || line.empty()) {
return true;
cout << line << " is a palindrome!" << endl;
} else {
return false;
cout << line << " is NOT a palindrome." << endl;
cout << "Enter new string: " << endl;
}
}
}
}
I remind everyone, I am pretty new to this stuff. I've read a few things already, but am still having a hard time wrapping my head around this.
Here's the general idea:
Iterative:
Initialize two pointers one pointer to the start and end of the string.
Compare the characters pointed, if different -> not palindrome.
Increase the start pointer and decrease the end pointer.
Repeat until start pointer >= end pointer.
Recursive (more difficult than iterative in this case):
End condition: A string of length zero or one is a palindrome.
A string is a palindrome if the first and last characters are the same and if the string without the first and last characters is a palindrome.
You can implement this recursive algorithm more efficiently by passing pointers to the first and last character in the string instead of copying the string between recursions.
Hope this helps :-)
I figure writing code is the best way to explain the two approaches. Is this code understandable?
bool iterative_palindrome(const string& str) {
int size = str.size();
for (int i=0; i<str.size()/2; i++) {
if (str[i] != str[size-i-1])
return false;
}
return true;
}
You call this like recursive_palindrome(str, 0).
bool recursive_palindrome(const string& str, int index) {
int size = str.size();
if (index >= size/2)
return true;
if (str[index] == str[size-index-1])
recursive_palindrome(str, index+1);
else
return false;
}
i'm writing my c++ project and in visual studio everything goes good but when i'm compiling it on ubuntu many things get wrong.
example:
int main (int argsNum, char* args[]){
Country* country = new Country("USA");
Military* military = new Military("Army",country);
Shalishut* shalishut = new Shalishut(military);
Manager* manager = Manager::GetInstance();
FileReader* fileReader = FileReader::GetInstance();
fileReader->ReadCityConfig(args,country);
fileReader->ReadRoadConfig(args,country);
fileReader->ReadMilitrayCampConfig(args,military);
military->ShowBases();
return 0;
}
void FileReader::ReadMilitrayCampConfig(char* args[], Military* military){
string line;
char inputFileName [MAX_FILE_NAME_LEN];
strcpy (inputFileName,args[3]);
ifstream myfile (inputFileName); //inputFileName
char* campName;
string cityName;
if (myfile.is_open()){
while (!myfile.eof()){ //until the end of file
getline (myfile,line); //separate each line.
if ((line.size() != 0) && (line[0] != '#')) {
campName = strtok(&line[0],",");
cityName = (string)strtok(NULL,",");
Shalishut::FixName(campName); Shalishut::FixName(&cityName[0]);
if (!(military->IsBaseExist(campName))){
if (military->GetCountry()->IsCityExist(cityName)){
Base* baseToAdd = new Base(campName,cityName);
if (baseToAdd != NULL){
military->AddBaseToMilitary(baseToAdd);
military->GetCountry()->FindCity(cityName)->AddBaseToCity(baseToAdd);
}
}
else cout << "ERROR: City named \"" << cityName << "\" does not exist, can't add base \"" << campName << "\" !" << endl<<endl;
}
else cout << "ERROR: Base Named \"" << campName << "\" is already exist in Military, can't create base!" << endl<<endl;
}
}
myfile.close();
}
else throw ExceptionMilitaryCampConfigFileFault(); /*cout << "ERROR: Unable to open MilitaryConfig file!"<< endl;*/
}
bool Country::IsCityExist(const string cityName){
map<string ,City*>::iterator itCities;
itCities = m_cities.find((string)cityName);
if (itCities != m_cities.end()) return true;
else return false;
}
void Shalishut::FixName(char* name){
int i;
name[0] = toupper(name[0]);
for (i=1 ; name[i] ; i++){
name[i] = tolower (name[i]);
}
}
}
The problem is that the program reads the cities and the roads, but when it reads the military camp i got:
" does not exist, can't add base "Hazerim" !
even though in the config file i have base in the same name.
remind: in visual studio it works perfectly!
Assuming the error message is actually ERROR: City named _____ does not exist, can't add base "Hazerim" I would look carefully at the capitalization/spelling of the cities and city-for-base in your inputs. They probably don't match.
Also using strtok on a std::string is just asking for trouble, as it's destructive and strings don't expect their internal state to be blown away randomly. There are method like find_first_of that will help you parse C++ strings.
Like others have said:
double check line endings (maybe run dos2unix on input files in lieu of a more robust / error=prone solution)
make sure the case of everything is correct, file names are case sensitive
be aware of where it is looking for files, make sure everything is in the CWD
I'd advise not messing around with std::string internals. I don't know that it's legal, and it certainly could cause problems. Use .c_str() to get the C-style string and copy it to a char [], or use string functions to parse the input.
To debug, put insome output statements so you can see what the string values are, or learn a bit about gdb and step through a short initialization run.
That cityname = (string)... is just plain ugly. Since you're not using cityname out of that scope, you can declare string cityname(...);, and cityname will always be initialized and will be defined close to where it's used.
First off, this is a "homework" question so vector libraries and string libraries are off limits. I'm trying to get to the basics of c++.
My intention with this code is to make and use an array of string arrays. A list of words in other words.
When I run this code I get a bunch of nonsense.
If there is a better way to make a list of words in c++, I would love to hear about it.
const int cart_length = 50;
const int word_length = 50;
int main()
{
char cart_of_names[cart_length][word_length];
float cart_of_costs[cart_length];
char name[word_length];
cout << "enter the name of the first item: ";
cin >> name;
for(int i=0; i<word_length; i++)
{
cart_of_names[0][i] = name[i];
}
cout << endl;
cout << "that is: ";
for(int x=0; x<word_length; x++)
{
cout << cart_of_names[0][x];
}
cout << endl;
return 0;
}
If the string entered is not 50 characters long (cart_length), then less than 50 characters will be valid in the name. You should have an if(cart_of_names[0][x]==0) break; in your second loop.
I don't exactly understand what you are looking for. Following code will help you to read and print a list of 50 words. Hope this would help you.
const int cart_length = 50;
const int word_length = 50;
int main()
{
char cart_of_names[cart_length][word_length];
float cart_of_costs[cart_length];
for(int i=0; i<cart_length; i++)
{
cout << "enter the name of the " << i + 1 << "th item: ";
cin >> cart_of_names[i];
}
cout << "that is: ";
for(int x=0; x < cart_length; x++)
{
cout << cart_of_names[x] << endl;
}
return 0;
}
Check out STLSoft's fixed_array_2d (and it's higher order siblings). There's a detailed discussion of how they're implemented for maximum performance in Matthew Wilson's Imperfect C++.
If you can't use std::string, at least look at the functions like strncpy() from C for your name copying. Also, you're forgetting that c-style strings are null terminated.
Unless you're forbidden to use STL (which would be just mean), just use std::list<std::string>. www.cplusplus.com has detailed descriptions and examples for those classes.
Otherwise, you're stuck with an array of char arrays: in that case, be prepared for a lot of buffer overflow errors. Look around on the above site for the char[] management functions (strncpy() and the like), they'll make your life a bit easier (but not a lot).
In C, the best way I found to conceptualize what you are trying to do is using an array of char*. Same effect, but if you start to work with it I believe you may find it is easier on the brain.
It looks pretty close to me. Strings in C are null-terminated, which means that the end of the string is indicated by a null character. In a sense, a string in C is really just an array of bytes.
When you do:
cout << "enter the name of the first item: ";
cin >> name;
If I enter the string "Book", in memory it'll look like something like:
|0|1|2|3|4|5..49|
|B|o|o|k|0|*HERE BE DRAGONS*
Well, really it will contain the ASCII values corresponding to those letters, but for our purposes, it contains those letters. There here be dragons is memory that that you didn't initialize, so it contains whatever garbage your platform sets it to.
So when you copy your string, you need to instead look for that 0 byte at the end of the string.
for(int i=0; name[i]!=0; i++)
{
cart_of_names[0][i] = name[i];
}
Then when you output it, you don't actually need to do it a character at a time. You can just do cout<<cart_of_names[0]. cout knows where the string ends because of that terminating null character.
If you use strcpy() instead of
cart_of_names[0][i] = name[i];
it may work better but I cringe just looking at all that code.
"If there is a better way to make a list of words in c++, I would love to hear about it."
Include #include <string> and use std::string. The std::string type is part of the C++ specification, I think.
#include <iostream>
#include <string>
int main(void) {
std::string list[7];
list[0] = "In C++";
list[1] = "you can use";
list[2] = "the `std::string` type.";
list[3] = "It removes";
list[4] = "many of the problems";
list[5] = "introduced by";
list[6] = "C-style strings.";
for (int k=0; k<7; k++) std::cout << list[k] << ' ';
std::cout << '\n';
return 0;
}