I have an assignment from school and have to read a comma delimited file and put each value in to a char*.
Ex)
File contains:
5,Justin,19,123-4567,etc..
char * firstValue = 5;
char * secondValue = Justin;
char * thirdValue = 123-4567; etc..
I cannot use std::string since we haven't learned it yet. I am supposed to do it with ifstream or other file streams. I have no idea how to do this
For homework assignments iterating over the char array in a for loop and every time a comma is encountered splitting the string will work (I won't give the code because it is a good exercise to try to work out yourself). But for real world use you should be aware that the CSV format is considerably more complex. Consider multiline entries or entries with an escaped comma and how they break the above solution. I strongly recommend using libcsv for any real world CSV parsing.
Related
I am trying to read only 10 or 100 line from my file. Is there any way that I can read certain line like this?
To read a single line from a file, use:
std::string text_from_file;
std::getline(text_file_stream, text_from_file);
In C++, to perform an action many times, we use a loop. So to read 10 lines from a file, we would use a for loop:
for (unsigned int i = 0U; i < 10U; ++i)
{
std::getline(text_file_stream, text_from_file);
}
Another method:
unsigned int lines_read = 0U;
while ((lines_read < 10) && (std::getline(text_file_stream, text_from_file)))
{
++lines_read;
}
To read 100 lines, you would change the constant from 10 to 100.
Skipping Lines
The fundamental issue with skipping lines or seeking to a given line, is that the text file has variable length records. You will have to read each line to figure out where the next one starts.
So the technique for skipping lines is to read a line into a text variable and ignore it, much like the examples above.
There are methods to speed this up, but they involve reading large blocks of data into memory or treating the file as memory (a.k.a. memory mapping). One issue with this technique is handling the case where the text line you want crosses the end of the buffer (it is not fully in the buffer). These techniques can be found in other posts on StackOverflow or on the Internet.
Reading until a delimiter
A delimiter is something that indicates the end of text. The standard delimiter for text files is a newline. You can read text until a comma, period tab or other delimiter, by using the 3rd parameter of std::getline.
const char delimiter = '.';
std::string text_from_file;
std::getline(text_data_stream, text_from_file, delimiter);
All this is available in good text books or a good online reference.
So I'm designing a game where data is stored and read with text files. But I need to be able to do this in a repeating fashioned so the names of the text files need to be different for each one procedurally. I figured that using variables as the name would solve this issue but I am having a hard time doing this and getting the compiler to work with it.
This is my current code to write 'hello' to a text file named 'test.txt':
ofstream myfile;
myfile.open("test.txt");
myfile << "hello";
myfile.close();
Now, I can replace "test.txt" with a single char* pointer or char array buffer, but I need to separate the name of the file with the .txt extension.
The idea that I had was to somehow combine a char* variable for a name reference (Such as galaxy, star, planet, etc.), plus a numeric value (would have to be a big number, perhaps a double or float), plus the ".txt" text extension. This would then be one char pointer or array that would be compatible with myfile.open. Any ideas? I'm open to changing the process of this as long as I get the same end result.
You should consider string instead of char* for your filename, as C++11 allows both types:
string myfilename="test";
myfile.open(myfilename+".txt");
myfile << "hello";
myfile.close();
To create more complex filenames you could then consider to use stringstreams: you can easily combine and format the filename using the usual output fromatting unctions/operators, and convert it to a string.
stringstream nn;
int counter=0;
nn<<myfilename<<counter<<".txt";
myfile.open(nn.str());
...
ifstream file;
file.open("file.csv");
string str;
while(file.good())
{
getline(file,str,',')
if (___) // string was split from delimiter
{
[do this]
}
else // string was split from eol
{
[do that]
}
}
file.close();
I'd like to read from a csv file, and differentiate between what happens when a string is split off due to a new line and what happens when it is split off due to the desired delimiter -- i.e. filling in the ___ in the sample code above.
The approaches I can think of are:
(1) manually adding a character to the end of each line in the original file,
(2) automatically adding a character to the end of each line by writing to another file,
(3) using getline without the delimiter and then making a function to split the resulting string by ','.
But is there a simpler or direct solution?
(I see that similar questions have been asked before, but I didn't see any solutions.)
My preference for clarity of the code would be to use your option 3) - use getline() with the standard '\n' delimiter to read the file into a buffer line by line and then use a tokenizer like strtok() (if you want to work on the C level) or boost::tokenizer to parse the string you read from the file.
You're really dealing with two distinct steps here, first read the line into the buffer, then take the buffer apart to extract the components you're after. Your code should reflect that and by doing so, you're also avoiding having to deal with odd states like the ones you describe where you end up having to do additional parsing anyway.
There is no easy way to determine "which delimiter terminated the string", and it gets "consumed" by getline, so it's lost to you.
Read the line, and parse split on commas yourself. You can use std::string::find() to find commas - however, if your file contains strings that in themselves contain commas, you will have to parse the string character by character, since you need to distinguish between commas in quoted text and commas in unquoted text.
Your big problem is your code does not do what you think it does.
getline with a delimiter treats \n as just another character from my reading of the docs. It does not split on both the delimiter and newline.
The efficient way to do this is to write your oen custom splitting getline: cppreference has a pretty clear description of what getline does, mimicing it should be easy (and safer than shooting from the hip, files are tricky).
Then return both the string, and information about why you finished your parse in a second channel.
Now, using getline naively then splitting is also viable, and will be much faster to write, snd probably less error prone to boot.
Suppose I have the following text:
My name is myName. I love
stackoverflow .
Hi, Guys! There is more than one space after "Guys!" 123
And also after "123" there are 2 spaces and newline.
Now I need to read this text file as it is. Need to make some actions only with alphanumeric words. And after it I have to print it with changed words but spaces and newlines and punctuations unchanged and on the same position. When changing alphanumeric words length remains same. I have tried this with library checking for alphanumeric values, but code get very messy. Is there anyother way?
You can read your file line-by-line with fgets() function. It will fill char array and you can work with this array, e.g. iterate over this array, split it into alnum words; change the words and then write fixed string into new file with "fwrite()" function.
If you prefer C++ way of working with files (iostream), you can use istream::getline. It will save spaces; but it will consume "\n". If you need to save even "\n" (it can be '\r' and '\r\n' sometimes), you can use istream::get.
Maybe you should look at Boost Tokenizer. It can break of a string into a series of tokens and iterate through them. The following sample breaks up a phrase into words:
int main()
{
std::string s = "Hi, Guys! There is more...";
boost::tokenizer<> tok(s);
for(boost::tokenizer<>::iterator beg = tok.begin(); beg != tok.end(); ++beg)
{
std::cout << *beg << "\n";
}
return 0;
}
But in your case you need to provide a TokenizerFunc that will break up a string at alphanumeric/non-alphanumeric boundaries.
For more information see Boost Tokenizer documentation and implementation of an already provided char_separator, offset_separator and escaped_list_separator.
The reason that your code got messy is usually because you didn't break down your problem in clear functions and classes. If you do, you will have a few functions that each do precisely one thing (not messy). Your main function will then just call these simple functions. If the function names are well chosen, the main function will become short and clear, too.
In this case, your main function needs to do:
Loop: Read every line of a file
On every line, check if and where a "special" word occurs.
If a special word occurs, replace it
Extra hints: a line of text can be stored as a std::string and can be read by std::getline(std::cin, line)
First off, I'm a complete beginner at C++.
I'm coding something using an API, and would like to pass text containing new lines to it, and have it print out the new lines at the other end.
If I hardcode whatever I want it to print out, like so
printInApp("Hello\nWorld");
it does come out as separate lines in the other end, but if I retrieve the text from the app using a method that returns a const char then pass it straight to printInApp (which takes const char as argument), it comes out as a single line.
Why's this and how would I go about to fix it?
It is the compiler that process escape codes in string literals, not the runtime methods. This is why you can for example have "char c = '\n';" since the compiler just compiles it as "char c = 10".
If you want to process escape codes in strings such as '\' and 'n' as separate characters (eg read as such from a file), you will need to write (or use an existing one) a string function which finds the escape codes and converts them to other values, eg converting a '\' followed by a 'n' into a newline (ascii value 10).