I'm trying to read in a list of files from another file. The file reading works but populating the array of char* isn't. It works for the first iteration but then gets a bad pointer on the next line. I tried with a vector of strings but was having problems, I think due to its destructor trying to free an argv.
char **datafiles = (char**)malloc(0);
int filecount = 0;
master.AddDataFiles(argv[1],datafiles,filecount);
int Manager::AddDataFiles(char *filename, char **filelist, int &filecount)
{
const int LINEMAX = 64;
struct stat info;
std::ifstream is(filename);
if (is.fail()) return 1;
char buffer[LINEMAX];
while(!is.eof())
{
is.getline(buffer,LINEMAX);
realloc(filelist,sizeof(char**) * (filecount + 1));
filelist[filecount] = (char*) malloc(std::strlen(buffer) + 1);
std::strcpy(filelist[filecount],buffer);
filecount++;
}
return 0;
}
Using realloc correctly is a bit tricky -- it can (and sometimes, but not always, will) return a different pointer than the one you passed to it, so you have to do something like this:
char **temp = realloc(filelist, sizeof(char**) * filecount+1);
if (temp != NULL)
filelist = temp;
else
failed_allocation();
Also note that your while (!file.eof()) is a classic mistake -- it won't sense the end of the file correctly.
I'd revisit the vector of strings option though. Perhaps you could post the code you had, and ask about whatever problem(s) you encountered with it. Getting it to work well will almost certainly be less work than fixing this, and the result will almost certainly be more solid and understandable.
Correct code for this would look something like:
std::vector<std::string> Manager::AddDataFiles(std::string const &filename) {
std::ifstream infile(filename.cstr());
std::vector<std::string> filenames;
std::string temp;
while (std::getline(infile, temp))
filenames.push_back(temp);
return filenames;
}
Why don't you use std::vector<std::string> instead of char**? This elegantly solves your problem!
If the filenames do not contain space then here is even more elegant solution (or else you can see Jerry's solution):
void Manager::AddDataFiles(const char *filename, std::vector<std::string> &filelist)
{
std::istream_iterator<string> start(std::ifstream(filename));
std::istream_iterator<string> end;
std::copy(start, end, std::back_inserter(filelist));
}
For this, you've to include these:
#include <iterator>
#include <algorithm>
#include <vector>
#include <string>
Use a std::string and std::vector<std::string>. It might also makes sense to have the file list a member.
#include <fstream>
#include <string>
#include <vector>
void Manager::AddDataFiles(const std::string& filename)
{
std::ifstream in(filename.c_str());
for( std::string line; std::getline(in, line); ) {
filelist.push_back(line);
}
}
Related
I have done a lot of reading on this topic online, and cannot figure out if my code is working. i am working on my phone with the c4droid app, and the debugger is nearly useless as far as i can tell.
as the title says, i need to separate 2 words out of one input. depending on what the first word is, the second may or may not be used. if i do not need the second word everything is fine. if i need and have the second word it works, or seems to. but if i need a second word but only have the first it compiles, but crashes with an out of range exception.
ActionCommand is a vector of strings with 2 elements.
void splitstring(std::string original)
{
std::string
std::istringstream OrigStream(original);
OrigStream >> x;
ActionCommand.at(0) = x;
OrigStream >> x;
ActionCommand.at(1) = x;
return;
}
this code will separate the words right?
any help would be appreciated.
more of the code:
called from main-
void DoAction(Character & Player, room & RoomPlayerIn)
{
ParseAction(Player, GetAction(), RoomPlayerIn);
return;
}
std::string GetAction()
{
std::string action;
std::cout<< ">";
std::cin>>action;
action = Lowercase(action);
return action;
}
maybe Lowercase is the problem.
std::string Lowercase(std::string sourceString)
{
std::string destinationString;
destinationString.resize(sourceString.size());
std::transform(sourceString.begin(), sourceString.end(), destinationString.begin(), ::tolower);
return destinationString;
)
void ParseAction(Character & Player, std::string CommandIn, room & RoomPlayerIn)
(
std::vector<std::string> ActionCommand;
splitstring(CommandIn, ActionCommand);
std::string action = ActionCommand.at(0);
if (ActionCommand.size() >1)
std::string action2 = ActionCommand.at(1);
skipping some ifs
if (action =="wield")
{
if(ActionCommand.size() >1)
DoWield(action2);
else std::cout<<"wield what??"<<std::endl;
return;
}
and splitstring now looks like this
void splitstring(std::string const &original, std::vector<std::string> &ActionCommand)
{
std::string x;
std::istringstream OrigStream(original);
if (OrigStream >>x)
ActionCommand.push_back(x);
else return;
if (OrigStream>>x)
ActionCommand.push_back(x);
return;
}
#include <sstream>
#include <vector>
#include <string>
std::vector<std::string> ActionCommand;
void splitstring(std::string const &original)
{
std::string x;
std::istringstream OrigStream{ original };
if(OrigStream >> x)
ActionCommand.push_back(x);
else return;
if(OrigStream >> x)
ActionCommand.push_back(x);
}
Another idea would be to use the standard library. You can split a string into tokens (using spaces as dividers) with the following function:
#include <string>
#include <vector>
#include <sstream>
#include <iterator>
inline auto tokenize(const std::string &String)
{
auto Stream = std::stringstream(String);
return std::vector<std::string>{std::istream_iterator<std::string>{Stream}, std::istream_iterator<std::string>{}};
}
Here, the result is created in place by using an std::istream_iterator, which basically stands in for the >> operation in your example.
Warning:
This code needs at least c++11 to compile.
could I ask for advice? Please, could someone give an example of code, which deletes spaces from lines of the first text file and saves "new text without spaces" into the second file. I understand how it be probably working, but I can not write it, because i am beginner in programing. Thanks for any advice.
My codes:
//read csv file
void readCSV(istream &input, vector< vector<string> > &output)
{
string csvLine;
while(getline(input, csvLine) )
{
istringstream csvStream(csvLine);
vector<string> csvColumn;
string csvElement;
while(getline(csvStream, csvElement) )
{
csvColumn.push_back(csvElement);
}
output.push_back(csvColumn);
}
}
//save all from csv to txt
void saveToTxt()
{
fstream file("file.csv", ios::in);
ofstream outfile;
outfile.open("file.txt");
typedef vector< vector<string> > csvVector;
csvVector csvData;
readCSV(file, csvData);
for(csvVector::iterator i = csvData.begin(); i != csvData.end(); ++i)
{
for(vector<string>::iterator j = i->begin(); j != i->end(); ++j)
{
outfile<<*j<<endl;
}
//code for deleting spaces, what i found, but i can't implement to above codes, coz my programming skill are not big
string s;
while (getline( cin, s ))
{
s.erase(
remove_if(
s.begin(),
s.end(),
ptr_fun <int, int> ( isspace )
),
s.end()
);
cout<<s<<endl;
I love solutions which won't qualify as a result for a homework assignment. Below is how I would write code for the specification, partly because I genuinely think that this is how it is to be done and partly to give others a bit of interesting reading. It contains all the necessary hints to create a teacher-friendly solution, too:
#include <algorithm>
#include <cctype>
#include <iterator>
int main() {
std::remove_copy_if(
std::istreambuf_iterator<char>(std::ifstream("in.txt").rdbuf()),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(std::ofstream("out.txt").rdbuf()),
[](unsigned char c){ return std::isspace(c) && c != '\n'; });
}
If you can't use a C++ 2011 compiler you'll need to replace the lambda function by an actual function with the same signature.
You can make this significantly simpler by using the same idea you have forremove_if, but instead applying it directly to determining whether to copy at all. something like the code below. Note: not tested, but I hope you get the idea.
#include <iostream>
#include <iterator>
#include <fstream>
#include <functional>
#include <cctype>
using namespace std;
int main(int argc, char *argv[])
{
ifstream is("file.csv", ios::in);
ofstream os("file.txt", ios::out|ios::trunc);
std::remove_copy_if(
istream_iterator<char>(is),
istream_iterator<char>(),
ostream_iterator<char>(os),
std::ptr_fun<int,int>(isspace));
os.close();
is.close();
return 0;
}
EDIT: I can't believe Deitmar and I had almost identical ideas.
I am having trouble getting started with a program. I need to read in each word from a file, then convert it to lower case. I would like to std::cout each word after I find it. I assume I need to use Cstr() some how. I am guessing I should use something like
ofs.open(infile.c_str());
but how to lower case?
string[i] = tolower(string[i]);
then,
std::cout << string[i];
Thanks for the help.
Here is a complete solution:
#include <ctype.h>
#include <iterator>
#include <algorithm>
#include <fstream>
#include <iostream>
char my_tolower(unsigned char c)
{
return tolower(c);
}
int main(int ac, char* av[]) {
std::transform(std::istreambuf_iterator<char>(
ac == 1? std::cin.rdbuf(): std::ifstream(av[1]).rdbuf()),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(std::cout), &my_tolower);
}
I found the answer to my own question. I really didn't want to use transform, but that does work as well. If anyone else stumbles across this here is how I figured it out...
#include <iostream>
#include <string>
#include <fstream>
int main()
{
std::ifstream theFile;
theFile.open("test.txt");
std::string theLine;
while (!theFile.eof())
{
theFile >> theLine;
for (size_t j=0; j< theLine.length(); ++j)
{
theLine[j] = tolower(theLine[j]);
}
std::cout<<theLine<<std::endl;
}
return 0;
}
int main()
{
ofstream of("xyz.txt");
clrscr();
ifstream inf;
char line;
inf.open("abc.txt");
int count=0;
while(!inf.eof())
{
inf.get(line);
if(line>=65 && line<=123)
{
cout<<line;
line=line-32;
of<<line;
count++;
cout<<line;
}
}
cout<<count;
getch();
return 0;
}
First of all, unless this is something like a homework assignment, it's probably easier to process one character at a time rather than one word at a time.
Yes, you have pretty much the right idea for converting to lower case, with the minor detail that you normally want to cast the input to unsigned char before passing it to tolower.
Personally, I'd avoid doing explicit input and output, and instead do a std::transform with a pair of istream_iterators and an ostream_iterator for the result.
I'm trying to load lines of a text file containing dictionary words into an array object. I want an array to hold all the words that start with "a", another one for "b" ... for all the letters in the alphabet.
Here's the class I wrote for the array object.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
class ArrayObj
{
private:
string *list;
int size;
public:
~ArrayObj(){ delete list;}
void loadArray(string fileName, string letter)
{
ifstream myFile;
string str = "";
myFile.open(fileName);
size = 0;
while(!myFile.eof())
{
myFile.getline(str, 100);
if (str.at(0) == letter.at(0))
size++;
}
size -= 1;
list = new string[size];
int i = 0;
while(!myFile.eof())
{
myFile.getline(str, 100);
if(str.at(0) == letter.at(0))
{
list[i] = str;
i++;
}
}
myFile.close();
}
};
I'm getting an error saying:
2 IntelliSense: no instance of overloaded function "std::basic_ifstream<_Elem, _Traits>::getline [with _Elem=char, _Traits=std::char_traits<char>]" matches the argument list d:\champlain\spring 2012\algorithms and data structures\weeks 8-10\map2\arrayobj.h 39
I guess it's requiring me to overload the getline function, but I'm not quite certain how to go about or why it's necessary.
Any advice?
the function for streams that deals with std::string is not a member function of istream but rather a free function it is used like so. (the member function version deals with char*).
std::string str;
std::ifstream file("file.dat");
std::getline(file, str);
It is worth noting there are better safer ways to do what you are trying to do like so:
#include <fstream>
#include <string>
#include <vector>
//typedeffing is optional, I would give it a better name
//like vector_str or something more descriptive than ArrayObj
typedef std::vector<std::string> > ArrayObj
ArrayObj load_array(const std::string file_name, char letter)
{
std::ifstream file(file_name);
ArrayObj lines;
std::string str;
while(std::getline(file, str)){
if(str.at(0)==letter){
lines.push_back(str);
}
}
return lines;
}
int main(){
//loads lines from a file
ArrayObj awords=load_array("file.dat", 'a');
ArrayObj bwords=load_array("file.dat", 'b');
//ao.at(0); //access elements
}
don't reinvent the wheel; checkout vectors they are standard and will save you a lot of time and pain.
Final try not to put in using namespace std that is bad for a whole host of reasons I wont go into; instead prefix std objects with std:: so like std::cout or std::string.
http://en.cppreference.com/w/cpp/container/vector
http://en.cppreference.com/w/cpp/string/basic_string/getline
http://en.cppreference.com/w/cpp/string
I have comma delimited strings I need to pull values from. The problem is these strings will never be a fixed size. So I decided to iterate through the groups of commas and read what is in between. In order to do that I made a function that returns every occurrence's position in a sample string.
Is this a smart way to do it? Is this considered bad code?
#include <string>
#include <iostream>
#include <vector>
#include <Windows.h>
using namespace std;
vector<int> findLocation(string sample, char findIt);
int main()
{
string test = "19,,112456.0,a,34656";
char findIt = ',';
vector<int> results = findLocation(test,findIt);
return 0;
}
vector<int> findLocation(string sample, char findIt)
{
vector<int> characterLocations;
for(int i =0; i < sample.size(); i++)
if(sample[i] == findIt)
characterLocations.push_back(sample[i]);
return characterLocations;
}
vector<int> findLocation(string sample, char findIt)
{
vector<int> characterLocations;
for(int i =0; i < sample.size(); i++)
if(sample[i] == findIt)
characterLocations.push_back(sample[i]);
return characterLocations;
}
As currently written, this will simply return a vector containing the int representations of the characters themselves, not their positions, which is what you really want, if I read your question correctly.
Replace this line:
characterLocations.push_back(sample[i]);
with this line:
characterLocations.push_back(i);
And that should give you the vector you want.
If I were reviewing this, I would see this and assume that what you're really trying to do is tokenize a string, and there's already good ways to do that.
Best way I've seen to do this is with boost::tokenizer. It lets you specify how the string is delimited and then gives you a nice iterator interface to iterate through each value.
using namespace boost;
string sample = "Hello,My,Name,Is,Doug";
escaped_list_seperator<char> sep("" /*escape char*/, ","/*seperator*/, "" /*quotes*/)
tokenizer<escaped_list_seperator<char> > myTokens(sample, sep)
//iterate through the contents
for (tokenizer<escaped_list_seperator<char>>::iterator iter = myTokens.begin();
iter != myTokens.end();
++iter)
{
std::cout << *iter << std::endl;
}
Output:
Hello
My
Name
Is
Doug
Edit If you don't want a dependency on boost, you can also use getline with an istringstream as in this answer. To copy somewhat from that answer:
std::string str = "Hello,My,Name,Is,Doug";
std::istringstream stream(str);
std::string tok1;
while (stream)
{
std::getline(stream, tok1, ',');
std::cout << tok1 << std::endl;
}
Output:
Hello
My
Name
Is
Doug
This may not be directly what you're asking but I think it gets at your overall problem you're trying to solve.
Looks good to me too, one comment is with the naming of your variables and types. You call the vector you are going to return characterLocations which is of type int when really you are pushing back the character itself (which is type char) not its location. I am not sure what the greater application is for, but I think it would make more sense to pass back the locations. Or do a more cookie cutter string tokenize.
Well if your purpose is to find the indices of occurrences the following code will be more efficient as in c++ giving objects as parameters causes the objects to be copied which is insecure and also less efficient. Especially returning a vector is the worst possible practice in this case that's why giving it as a argument reference will be much better.
#include <string>
#include <iostream>
#include <vector>
#include <Windows.h>
using namespace std;
vector<int> findLocation(string sample, char findIt);
int main()
{
string test = "19,,112456.0,a,34656";
char findIt = ',';
vector<int> results;
findLocation(test,findIt, results);
return 0;
}
void findLocation(const string& sample, const char findIt, vector<int>& resultList)
{
const int sz = sample.size();
for(int i =0; i < sz; i++)
{
if(sample[i] == findIt)
{
resultList.push_back(i);
}
}
}
How smart it is also depends on what you do with those subtstrings delimited with commas. In some cases it may be better (e.g. faster, with smaller memory requirements) to avoid searching and splitting and just parse and process the string at the same time, possibly using a state machine.