How would you read a string if it was no longer than 20 characters and could contain spaces in C++? For example I have this kind of file:
3
Namama 4 5 12
Cool 4 2 34
Not So Cool 4 2 45
I want to write it in the most basic way possible. And I would prefer using only C++ (no C thingies) in this case. This means I want to store it in std::string.
I could use istream::get or istream::getline but then I have to avoid the new line characters. This is problematic (and the code I'm writing is going to be shown to beginners).
What are your solutions for this problem?
Edit:
As asked I'm going to tell you what I have tried but I don't think it's going to be any good. I am not a C++ beginner and usually I would have used something like istream::get and char array but removing new line characters might seem too techy for some people.
So... Either reading char array or std::string using istream::operator>> fails because it stops reading when they see space character (and I may have to read several words). This means the following code fails:
char name[21];
std::cin >> name;
or...
std::string name;
std::cin >> name;
Another thing is that new line characters differ from system to system and actually Windows use two of them by default and I have tried using istream::ignore with 2 as an argument on Windows and that was the only way to ignore the new line and I came to conclusion that it's because Windows use two characters for a new line mark. That means it wouldn't work on Linux and it would have to be more complex... Again - bad for beginners.
If you want to read exactly 20 characters,
for(int i=0; i<3; ++i) {
std::string result;
int one, two, three;
result.resize(20);
if (!std::cin.read(&result[0], 20)) throw std::runtime_error("reading buffer failed!");
if (!std::cin >> one >> two >> three) throw std::runtime_error("reading numbers failed!");
if (!std::cin.ignore(1000, '\n')) throw std::runtime_error("ignore failed!");
}
If you don't want exactly 20 characters, how do you know when you've reached the end of the string?
One way to accomplish this would be to use istream::read into a temporary buffer of sufficient size, and then use that buffer to initialize a std::string.
char name[21];
std::cin.read(name, 20);
std::string nameString(name); // If you want it in a std::string
This reads exactly 20 characters, newlines and everything. If it's for an exercise, then this is at the very least very simple. If you sometimes want less than 20 characters, it is possible to determine how many extra were read and back the get pointer by std::istream::unget or equivalent.
Related
4;
Spadina;76 156
Bathurst;121 291
Keele;70 61
Bay;158 158
This is what file contains in it. I need to read them and save them into variables.
4 is for dynamic memory allocation. 4 means there are 4 stations.
Spadina, Bathrust, etc.. they are station names. first number, which comes right after station names, is number of student passes and the second number is number of adult pass.
So, basically I have 4 variables and they are;
int numberOfStation;
int studentPass;
int adultPass;
string stationName;
I spent 4 hours but still cannot read the file and save it into variable
Thank you.
A possible solution is to read every line with e.g. std::getline then parse each such line string. You'll use the appropriate methods of std::string to search inside it (with find) and split it (with substr). You might also access some individual character in that string using at or the [] operator of std::string; alternatively, you might perhaps parse each line -or relevant parts of them- using std::istringstream (I am not sure it is appropriate in your case). You might be interested by std::to_string...
An important thing is to define exactly (not only thru examples) the possible acceptable inputs. You could for example use some EBNF to formalize that. You should probably care about character encoding (try first by assuming a simple single-byte encoding like ASCII, then later consider UTF-8 if your system uses it).
For example, can the station names (I guess you talk about subway stations) contain digits, or spaces, or underscores, or commas, etc.... ? Could they be French names like Hôtel de Ville (a metro station in central Paris) or Saint-Paul or Bourg-La-Reine (where I am), or Russian names like Молодёжная in Moscow? (I guess that station names in Tokyo or in Jerusalem might be even funnier to parse).
BTW, explicitly entering the number of entries (like your initial 4) is very user-unfriendly. You could have some lexical conventions and e.g. use some tagging or separators.
At last, you might want to keep the information for every travel. Then you'll probably need to define some struct or class (not simply four scalar variables). At that point your program is becoming more interesting!
First group your variables in struct.
struct MyStruct
{
int studentPass;
int adultPass;
string stationName;
};
Now read size of struct in file and allocate it dynamically
MyStruct *p;
s >> N;
p = new MyStruct[N];
Now in for loop you read string with delimiter ';' and other two vars are ints
for (int i = 0; i < N; i++)
{
getline(s, p[i].stationName, ';');
s >> p[i].studentPass >> p[i].adultPass;
}
Where var s is istream type of variable with flag std::in
I recommend that you create a struct to hold your stations:
struct station{
string _stationName;
int _studentPass;
int _adultPass;
};
Then create an operator to use with your struct (props to Sly_TheKing for the getline idea):
std::istream& operator>>(std::istream& is, station& rhs){
getline(is, rhs._stationName, ';');
is >> rhs._studentPass >> rhs._adultPass >> ws;
return is;
}
Say that your ifstream is called foo. You can read these into a vector like this:
foo.ignore(numeric_limits<streamsize>::max(), '\n');
vector<station> bar{istream_iterator<station>(foo), istream_iterator<station>()};
I was doing a relatively simple string problem in UVa's online judge to practice with strings since I've been having a hard time with them in C. The problem basically asks to check if a string B contains another string A if you remove the 'clutter' and concatenate the remaining characters, for example if "ABC" is contained in "AjdhfmajBsjhfhC" which in this case is true.
So, my question is how can I efficiently allocate memory for a string which I don't know its length? What I did was to make a string really big char Mstring[100000], read from input and then use strlen(Mstring) to copy the string the a properly sized char array. Something like :
char Mstring[100000];
scanf("%s",Mstring);
int length = strlen(Mstring);
char input[length+1]={0};
for(int i = 0; i<length;i++){
input[i]=Mstring[i];
}
Is there a better/standard way to do this in C? I know that C does not has a great support for strings, if there is not a better way to do it in C maybe in C++?
If you have the option of using C++ (as you mentioned), that is going to make your life a lot easier. You can then use a STL string (std::string) which manages dynamically sized strings for you. You can also drop the old scanf() beast and use std::cin.
Example:
#include <iostream>
#include <string>
void main()
{
std::string sInput;
std::getline(std::cin, sInput);
// alternatively, you could execute this line instead:
// std::cin >> sInput;
// but that will tokenize input based on whitespace, so you
// will only get one word at a time rather than an entire line
}
Describing how to manage strings that can grow dynamically in C will take considerably more explanation and care, and it sounds like you really don't need that. If so, however, here is a starting point: http://www.strchr.com/dynamic_arrays.
I am trying to learn some techniques of reading from files in C++ and I came up with this example.
Assume the following is the content of the txt file that I want to read from
A
1 2 3
4 5 6
7 8 9
B
1 2 3
4 5 6
7 8 9
So, what I want to do here is that if we read A, then we start to read the matrix A from below and store them into a[i][j]. And the same for B. In other circumstances we take them as exception, which we don't care here now.
The problem for me now is mixed reading. I know how to read integer and how to read strings from the file separately, like the stupid way while(fin>>it). but can anyone tell me a fast way of this kind of mixed reading that I don't have to declare several reading variables (type) such as string and int?
For example, I only know how to read integer in a whole line and don't know the newline handler, which means I don't know how to recognize if we reached the end of the line or something like this:
ifstream fin;
fin.open(infilename);
int it;
int arr[3][3];
int i=0, j=0;
while(fin>>it){
arr[i][j]=it;
`\\I am confused at this place and don't know how to write the condition`
}
fin.close();
Moreover, since there are both char and int type, do I have to declare char? and how does fin>>char really work? reading char by char in a line or something else?
I'll really appreciate if someone can guide me on this! Thanks!
You shouldn't have to worry about newlines as >> skips whitespace and newlines. However, putting fin>>it in the while condition complicates things if you want different data types. You could instead read in to a character to represent each matrix, then within the loop read from fin to the matrix:
char c;
int mat[3][3];
while (fin>>c){
//save c somewhere
fin>>mat[0][0]>>mat[0][1]>>mat[0][2];
fin>>mat[1][0]>>....;
fin>>mat[2][0]>>....;
//store mat
}
I have to read huge lines of strings from stdin so time is a critical issue. Strings are on consecutive lines and have no spaces so I can simply use while(cin>>str) { //code } but this is extremely slow. I have heard that scanf is much more faster than cin but if I use scanf("%s,str) I think that str is treated as char* and not a C++ string so I can't use the STL. I could take input as char* and copy all the chars into a C++ string but IMO that will also be slow.
Is there a way to get input using scanf or something but still get a C++ string as a result?
If you know the average or maximum size of the text, you create std::string with a pre-allocated size. One area occupying a lot of time is the memory (re) allocation by std::string.
cin >> str is the closest thing you'll find in STL to scanf("%s, str"). The only reason scanf would be faster than cin is because it would be giving you a char* instead of a string, and while you can create a new string from the char* by just passing them in to the string() constructor, that would be almost the same thing as using cin >> str.
You can use getline:
for (std::string line; getline(std::cin, line); ) {
do_something_with(line);
}
I don't know if it is any faster than cin >> line, but it might be, since it doesn't need to deal with whitespace other than newlines. But I don't believe this is as significant as the overhead of sentry construction.
I was trying out a few file reading strategies in C++ and I came across this.
ifstream ifsw1("c:\\trys\\str3.txt");
char ifsw1w[3];
do {
ifsw1 >> ifsw1w;
if (ifsw1.eof())
break;
cout << ifsw1w << flush << endl;
} while (1);
ifsw1.close();
The content of the file were
firstfirst firstsecond
secondfirst secondsecond
When I see the output it is printed as
firstfirst
firstsecond
secondfirst
I expected the output to be something like:
fir
stf
irs
tfi
.....
Moreover I see that "secondsecond" has not been printed. I guess that the last read has met the eof and the cout might not have been executed. But the first behavior is not understandable.
The extraction operator has no concept of the size of the ifsw1w variable, and (by default) is going to extract characters until it hits whitespace, null, or eof. These are likely being stored in the memory locations after your ifsw1w variable, which would cause bad bugs if you had additional variables defined.
To get the desired behavior, you should be able to use
ifsw1.width(3);
to limit the number of characters to extract.
It's virtually impossible to use std::istream& operator>>(std::istream&, char *) safely -- it's like gets in this regard -- there's no way for you to specify the buffer size. The stream just writes to your buffer, going off the end. (Your example above invokes undefined behavior). Either use the overloads accepting a std::string, or use std::getline(std::istream&, std::string).
Checking eof() is incorrect. You want fail() instead. You really don't care if the stream is at the end of the file, you care only if you have failed to extract information.
For something like this you're probably better off just reading the whole file into a string and using string operations from that point. You can do that using a stringstream:
#include <string> //For string
#include <sstream> //For stringstream
#include <iostream> //As before
std::ifstream myFile(...);
std::stringstream ss;
ss << myFile.rdbuf(); //Read the file into the stringstream.
std::string fileContents = ss.str(); //Now you have a string, no loops!
You're trashing the memory... its reading past the 3 chars you defined (its reading until a space or a new line is met...).
Read char by char to achieve the output you had mentioned.
Edit : Irritate is right, this works too (with some fixes and not getting the exact result, but that's the spirit):
char ifsw1w[4];
do{
ifsw1.width(4);
ifsw1 >> ifsw1w;
if(ifsw1.eof()) break;
cout << ifsw1w << flush << endl;
}while(1);
ifsw1.close();
The code has undefined behavior. When you do something like this:
char ifsw1w[3];
ifsw1 >> ifsw1w;
The operator>> receives a pointer to the buffer, but has no idea of the buffer's actual size. As such, it has no way to know that it should stop reading after two characters (and note that it should be 2, not 3 -- it needs space for a '\0' to terminate the string).
Bottom line: in your exploration of ways to read data, this code is probably best ignored. About all you can learn from code like this is a few things you should avoid. It's generally easier, however, to just follow a few rules of thumb than try to study all the problems that can arise.
Use std::string to read strings.
Only use fixed-size buffers for fixed-size data.
When you do use fixed buffers, pass their size to limit how much is read.
When you want to read all the data in a file, std::copy can avoid a lot of errors:
std::vector<std::string> strings;
std::copy(std::istream_iterator<std::string>(myFile),
std::istream_iterator<std::string>(),
std::back_inserter(strings));
To read the whitespace, you could used "noskipws", it will not skip whitespace.
ifsw1 >> noskipws >> ifsw1w;
But if you want to get only 3 characters, I suggest you to use the get method:
ifsw1.get(ifsw1w,3);