Get the line after white-space in C++ - c++

I would like to read last few characters in each line of a text file. How would I do that in better optimized C++ code? For example my file has names and ages separated by a white-space of 100 students with new line for each student, so I only want to read the content after white space, i.e age and do further comparison.
File classlist.txt
Tanya 24
Max 16
Dic 21
Code I wrote so far
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
ifstream class_details("classlist.txt");
if (!class_details.is_open())
{
cout<< "error"<<'\n';
return;
}
string data;
% From here I am thinking on how to get the data after first whitespace
% in each line
class_details.close();
return 0;
}

As #Joachim notes, the input operator >> separates on white-space.
You can use it as follows:
string name;
int age;
while (class_details >> name >> age) {
// Use name & age here.
}
class_details >> name extracts a word (non-white-space characters) from the input stream, and stores it in the name variable, and returns the original stream object class_details. This allows chaining the input extraction ...
(Since class_details >> name, returns class_details, we can further apply the >> to the return value of the expression to extract more information.)
>> age continues the extraction, reading (since age is an integer variable) an integer from the input stream, and storing it in the age variable, and (continuing the chain) returns the original stream object class_details.
The expression in the while(...) loop returns (due to the way the >> operators allows chaining) the original input stream, which when evaluated in boolean context, returns the stream's good() state. If the end of stream has been encountered, or an invalid extraction was attempted, or any other problem, good() would return false, and exit the loop.
So, without chaining (but using the , operator instead), the above could be rewritten more verbosely as:
string name;
int age;
while(class_details >> name, class_details >> age, class_details.good()) {
// Use name & age here.
}
EDIT
If the goal is to ignore the name field (skip the name without extracting it into a variable), you could use the ignore() method to skip everything up to the next space character:
int age;
while (class_details.ignore(numeric_limits<streamsize>::max(), ' ') >> age >> ws) {
// Use age here.
}
The >> ws is being used to consume any white-space from the end of the "age" field to the beginning of the name field on the next line ... specifically, the "new line" character.
You can use number smaller than numeric_limits<streamsize>::max(), if desired. Just choose something longer than your longest name.

With my less knowleage
1 : copy data every time to compare if same break
while(1)
{
fgets(str1, sizeof(str1), f); // scanf what you want
if( strcmp(str1,str2)==0 )break;
strcpy(str2,str1);
}
2 : at last add some thing weird
while(1)
{
fgets(str1, sizeof(str1), f); // scanf what you want
if( str1 == "key" )break;
}

Related

A simple way to read TXT config files in C++

this question seems to be already asked but I did not find any convenient solution for my case.
I have the following TXT config file to read in C++:
--CONFIGURATION 1 BEGIN--
IP address: 192.168.1.145
Total track length [m]: 1000
Output rate [1/s]: 10
Time [s]: 1
Running mode (0=OFF 1=ON): 1
Total number of attempts: 10
Mode (0=OFF, 1=BEG, 2=ADV, 3=PROF): 1
--Available only for Administrators--
Variable 1 [mV]: 2600
Gain 1 [mV]: 200
Position tracking (0=OFF 1=ON): 0
Coefficient 2 [V]: 5.2
--CONFIGURATION 1 END--
--CONFIGURATION 2 BEGIN--
Max track distance [m]: 10000
Internal track length [m]: 100
Offset distance [mV]: 1180
GAIN bias [mV]: 200
Number of track samples: 1000
Resolution (1 or 2) [profile]: 1
--CONFIGURATION 2 END--
I need to store only the value at the end of each line that could be a string (in the case of the IP address), an int, a float or a bool inside a struct. In C there is a very simple solution, I read each single line using an expression as follows:
if(!fscanf(fp, "%*s %*s %*s %*s %d\n", &(settings->trackLength))) {
printf("Invalid formatting of configuration file. Check trackLength.\n");
return -1;
}
The %*s allows to discard the label of the line and the spaces before the interested value. I use fgets to skip the empty lines or the titles. This way works also in C++. Is it good to leave my code as is or do you see a better and simple way to do this in C++?
Thank you very much.
Also in C++ it is easy to split a line. I have already provided several answers here on SO on how to split a string. Anyway, I will explain it here in detail and for your special case. I also provide a full working example later.
We use the basic functionality of std::getline which can read a complete line or the line up to a given character. Please see here.
Let us take an example. If the text is stored in a std::string we will first put it into a std::istringstream. Then we can use std::getline to extract the data from the std::istringstream. That is always the standard approach. First, read the complete line from a file using std::getline, then, put it in a std::istringstream again, to be able extract the parts of the string again with std::getline.
If a source line looks like that:
Time [s]: 1
We can obsserve that we have several parts:
An identifier "Time [s]",
a colon, which acts as a separator,
one or more spaces and
the value "1"
So, we could write something like this:
std::string line{}; // Here we will store a complete line read from the source file
std::getline(configFileStream, line); // Read a complete line from the source file
std::istringstream iss{ line }; // Put line into a istringstream for further extraction
std::string id{}; // Here we will store the target value "id"
std::string value{}; // Here we will store the target "value"
std::getline(iss, id, ':'); // Read the ID, get read of the colon
iss >> std::ws; // Skip all white spaces
std::getline(iss, value); // Finally read the value
So, that is a lot of text. You may have heard that you can chain IO-Operations, like in std::cout << a << b << c. This works, because the << operation always returns a reference to the given stream. And the same is true for std::getline. And because it does this, we can use nested statements. Meaning, we can put the second std::getline at this parameter position (actually the first paramater) where it expects a std::istream. If we follow this approach consequently then we can write the nested statement:
std::getline(std::getline(iss, id, ':') >> std::ws, value);
Ooops, whats going on here? Let's analyze from inside out. First the operation std::getline(iss, id, ':') extracts a string from the std::istringstream and assign it to variable "id". OK, understood. Remember: std::getline, will return a reference to the given stream. So, then the above reduced statement is
std::getline(iss >> std::ws, value)
Next, iss >> std::ws will be evaluated and will result in eating up all not necessary white spaces. And guess what, it will return a refernce to the gievn stream "iss".
Statement looks now like:
std::getline(iss, value)
And this will read the value. Simple.
But, we are not finished yet. Of course std::getline will return again "iss". And in the below code, you will see something like
if (std::getline(std::getline(iss, id, ':') >> std::ws, value))
which will end up as if (iss). So, we use iss as a boolean expression? Why does this work and what does it do? It works, because the bool operator of the std::stream is overwritten and returns, if the state is OK or has a failure. Please see here for an explanation. Always check the result of any IO-operation.
And last but not least, we need to explain the if statement with initializer. You can read about it here.
I can write
if (std::string id{}, value{}; std::getline(std::getline(iss, id, ':') >> std::ws, value)) {
which is the similar to
std::string id{}, value{};
if (std::getline(std::getline(iss, id, ':') >> std::ws, value)) {
But the first example has the advantage that the defined variables will be only visible within the if-statements scope. So, we "scope" the variable as narrow as possible.
You should try to do that as often as possible. You should also always check the return state of an IO-operation by applying if to a stream-operation, as shown above.
The complete program for reading everything will then just be a few lines of code.
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <iomanip>
int main() {
// Open config file and check, if it coul be opened
if (std::ifstream configFileStream{ "r:\\config.txt" }; configFileStream) {
// Here we wills tore the resulting config data
std::unordered_map<std::string, std::string> configData;
// Read all lines of the source file
for (std::string line{}; std::getline(configFileStream, line); )
{
// If the line contains a colon, we treat it as valid data
if (if (line.find(':') != std::string::npos)) {
// Split data in line into an id and a value part and save it
std::istringstream iss{ line };
if (std::string id{}, value{}; std::getline(std::getline(iss, id, ':') >> std::ws, value)) {
// Add config data to our map
configData[id] = value;
}
}
}
// Some debug output
for (const auto& [id, value] : configData)
std::cout << "ID: " << std::left << std::setw(35) << id << " Value: " << value << '\n';
}
else std::cerr << "\n*** Error: Could not open config file for reading\n";
return 0;
}
For this example I store the ids and values in a map, so that they can be accessed easily.

cin is not accepting input with space in them in C++?

#include <iostream>
using namespace std;
int main(){
string doi, name, address, phone;
cout<<"Please provide these credentials:\n";
cout<<"1.Date of issue\n";
cin>>doi;
cout<<"2.Name\n";
cin>>name;
cout<<"3.Address\n";
cin>>address;
cout<<"4.Phone Number\n\n";
cin>>phone;
return 0;
}
When I am giving name input with spaces, for eg ("John Doe"), instead of storing this value is name it split value by space and store "John" in name and "Doe" in address.
If you have spaces in the string you want to read, you could use std::getline like this:
std::getline(std::cin, name);
instead of the line:
std::cin >> name;
Note that the getline will read all characters up to a newline character.
Also, please avoid using namespace std;.
You should use getline() in place of cin when you need to input strings with spaces.
In your case the syntax will be
string name;
getline(cin,name);
for more info on getline https://www.geeksforgeeks.org/getline-string-c/
There's a lot of muddled terminology in the comments and the answers. std::cin is an object; it doesn't do anything on its own.
Functions that read from input streams fit into one of two categories: they do formatted input or unformatted input. Formatted input functions translate the text that they get from the input stream (here, std::cin) into the data type that they're trying to read:
int i;
std::cin >> i; // operator>> reads text and translates it into an integer value
Formatted input functions begin by skipping whitespace, then they read characters and translate them; when the function encounters a character that isn't valid for the type that they're reading, or when they see whitespace, they stop. So in the example above, if you typed " 32 ", the stream extractor would skip the leading space, read the 3 and the 2, see the following space, and stop reading. The value stored into i would be 32.
std::string data;
std::cin >> data;
Here, if you type "Hello, world", the stream extractor (operator>>) will read up to the space, and store "Hello," in data.
If you want to read whitespace as well as non-whitespace you need an unformatted input function:
std::string data;
std::getline(std::cin, data);
Here, the call to getline reads text from std::cin up to the first newline character or to the end of the input stream. So if you typed " 32 " for this code, data would hold the text " 32 ". If you typed "Hello, world", data would hold the text "Hello, world".
And note that if you mix formatted input functions with unformatted input functions you have to be careful about leftover whitespace:
int i;
std::string data;
std::cin >> i;
std::getline(std::cin, data);
If you typed "32 Hello, world" on a single line, i would get the 32, and data would get " Hello, world".
On the other hand, if you type two lines of input, the first with "32" and the second with "Hello, world", you'll get 32 in i, but data will be empty. That's because the stream extractor read the 3 and the 2, then saw a newline character, and stopped, leaving the newline in the input buffer. std::getline then read that newline character and it, too, stopped. But it read the entire line: it swallowed the newline character.
So when your code switches from formatted input to unformatted you have to deal with whitespace characters remaining in the input buffer. If you want to read them, fine; but if you don't, you need to remove them:
int i;
std::string data;
std::cin >> i;
std::getline(std::cin, data); // read the rest of the line
std::getline(std::cin, data); // read the next line of text
A better approach is to do that cleanup with something like std::cin.ignore(42, '\n');. std::ignore is an unformatted input function; in this call it reads up to 42 characters, looking for a newline character. It stops reading when it has read 42 characters, sees a newline character, or hits the end of the input. That's better than using std::getline(std::cin, data) because it doesn't store the text into data, which could require a bunch of resizing if there's a lot of text in the remainder of the line. The more usual form for that call is to pass std::numeric_limits<int>::max() as the size argument; that's a special case, and it puts no limit on the number of characters to be read. So std::cin.ignore(std::numeric_limits<int>::max(), '\n'); will read characters until it finds a newline or hits the end of the input.
int i;
std::string data;
std::cin >> i;
std::cin.ignore(std::numeric_limits<int>::max(), '\n'); // flush the rest of the line
std::getline(std::cin, data); // read the next line of text

How can we take out the multiple integer numbers in the string array or string and assign them to different int data type?

I am new to C++ and I am reading in a text file. The content of text file is like:
$ (first line)
2 (second)
MY NAME IS (whatever sentence with 10 or below characters)(third)
12 21 (forth)
22 22 (fifth)
221 (sixth)
fly jump run (seventh)
fish animal (eighth)
So I need to read all of these and store them into different variables line by line and so far I'd manage to store them into string array line by line but how can I store the numbers like 12 21 in forth line into 2 different integer variables such as int b and int c?
and also like last two line
how can I store the fly jump run fish animal into 5 different string variables respectively?
Basically Now I am putting them into a string array line by line and trying to access them and take them out of the array and store it.
if (file.is_open()){
cout<<"Congratulations! Your file was successfully read!";
while (!file.eof()){
getline(file,line);
txt[i]=line;
i++;
}
}
Just want to store every line into variables based on their data type.
The streams support streaming the content directly into the basic data types (int, double etc.). So the istream::operator>>(int&) does the work for you.
The below small sample class demonstrates it by reading your sample file into the members -- hope that helps:
class Creature
{
public:
void read(istream& stream)
{
string line;
stream.ignore(10, '\n'); // skip line 1 (= $)
stream >> m_integers[0]; // line 2 = single int
stream.ignore(1, '\n'); // skip end of line
getline(stream, m_sentence); // get the full sentence line ..
// and the rest ... we can read that in a single code line ...
stream >> m_integers[1] >> m_integers[2] >> m_integers[3] >> m_integers[4]
>> m_integers[5] >> m_whatCanIdDo[0] >> m_whatCanIdDo[1] >> m_whatCanIdDo[2] >> m_whatIAm[0] >> m_whatIAm[1];
}
private:
string m_sentence;
int m_integers[6];
string m_whatCanIdDo[3];
string m_whatIAm[2];
};
Calling the function:
int main()
{
ifstream file;
file.open("creature.txt");
Creature cr;
cr.read(file);
file.close();
}
There are several ways of doing this, but one of the most straightforward is to use a stringstream.
To do this, copy the lines you want to tokenize from your txt array into a stringstream. Use the stream extratction operator (>>) to read out each word from that line, separated by a space, into a separate variable.
//Required headers
#include <string>
#include <sstream>
...
string word1, word2;
stringstream words(txt[lineNumber]);
words >> word1 >> word2;
//Process words
For each line you tokenize, you'll have to reset the stream.
//Read in next line
lineNumber++;
//Reset stream flags
words.clear();
//Replace the stream's input string
words.str(txt[lineNumber]);
words >> word1 >> word2;
//Process new words
You can use the same process for both integers and strings. The stream extraction operator will automatically convert strings to whatever data type you give it. However, it's up to you to make sure that the data it's trying to convert is the correct type. If you try to write a string to an int using a stringstream, the stringstream will set a fail bit and you won't get any useful output.
It's a good idea to write your input to a string, and then check whether that string is, in fact, a number, before trying to write it to an integer. But that's an entirely different topic, there are many ways to do it, and there are several other questions on this site that cover it.

split string - multiple delimiter C++

so this user input all in one line string , i need to parse the input into two categories: user age and user name.
for example , the user input -->> [23:Frank] [15:Jack] [45:] [33:sofia] []
in this case i have more than one argument (delimiter, total of 3 ) which are [:] , in addition i need to get user input and stop looping once i encounter the [] at the end.
this is what i was thinking :
string input;
vector<string> age;
vector<string> name;
cin >> input;
while (input != "[]")
{
get age between first [ and :
assign to age variable
get name between : ]
assign to user name variable
................
}
also - what if one of the brackets is missing a name , how can assign a blank name and skip that part in order to process the rest (meaning i will output age with no name next to it).
any suggestions regarding how to get and process the data.
i saw some advance stuff like Toknizing and booster which are advance for my course, that's why i was thinking about straight forward getline and parse functions.
Thank you.
Read in token like you are currently doing with cin
test for [] as you are doing with the while loop
For the inside the loop, here are a few things to help you out:
std::string's front and back functions are perfect for ensuring that input starts with [ and ends with ]
std::string's substr function is perfect for trimming off the [] so you can easily ignore them for the rest of the parsing
std::stringstream allows you to call make a stream that only contains your trimmed input.
std::getline(stream, string, char) will read all characters it finds up to the char parameter or the end of the stream and stuff the results in the string parameter and then discard the char it found so you won't trip over it parsing the rest of the input.
strtoul will convert a string into a number and tell you if it failed. It will not accept negative numbers so you can catch people trying to trick your program.
getline(stream, string) will read the stream until it hits an end of line marker. Perfect for reading the rest of a stream that contains no end of lines.
Using strtoul:
char * endp;
unsigned long agenum strtoul(agestr.c_str(), // turn string into old c-style string
&endp, // will be updated with the end of the char after the last number
10); // base ten numbers
if (endp != '\0') // old c-strings always end on a null (numerical zero). If strtoul
// didn't end on a null, the string wasn't a valid number.
{
//not a number
}
Ok , so THANKS for people who helped or at least tried to help!!
what i end up doing for this part was as follows:
read in each string at once
use the find function in order to locate my delimiters ( which are in this case [ : ] )
return positions per each perimeter based on my argument (each pair will hold either the beginning and the end for age || name)
pass those arguments results to truncate the string by using substr function, then assign into each variables.
while (true)
{
string myInput;
cin >> myInput;
while (myInput != "[]")
{
int age_beg = myInput.find('[') + 1 ;
int age_end = myInput.find(':', age_beg);
string age = myInput.substr(age_beg, (age_end - age_beg));
int name_beg = age_end + 1;
int name_end = myInput.find(']', name_beg);
string name = myInput.substr(name_beg, (name_end - name_beg));
cout << "NAME : " << name << " AGE : " << age << endl;
cin >> myInput;
}
}
Hope this will help others with the same question in the future !!

getline to split string manipulation error

Hey guys so I have an assignment for class where I have to split a string and manipulate it. However, when I try to split the string and assign it to an array only the first element comes and the other two don't. Please help.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int main()
{
string str;
cout<<"Enter a first name, middle initial, last name: ";
cin>> str;
string word;
string array[3];
stringstream stream(str);
int counter = 0;
while( getline(stream, word, ' ') )
{
cout<<word;
array[counter] = word;
counter++;
}
cout<<"The "<<array[0].length()<<" characters of the first name are: "<<array[0]<<endl;
cout<<"The "<<array[2].length()<<" characters of the last name are: "<<array[2]<<endl;
string newstring = array[2]+", "+array[0]+" "+array[1];
cout<<"In the phone book, the name would be: "<<newstring<<endl;
cout<<"The length of the name is: "<<newstring.length()<<endl;
cout<<"The comma is at position: "<<newstring.find(",")<<endl;
array[0].swap(array[2]);
cout<<"After the swap, the last name is "<<array[2]<<" and the first name is "<<array[0];
system("pause");
return 0;
}
There are a few blatant errors in your code:
You need to always check your input after trying to read! You do that using the while-loop but you also need to verify that you actually successfully read the string first.
It seems you are mixing the what the input operator for std::string and std::getline() are doing: the input operator reads the first word after skipping leading spaces while std::getline() read, well, a line (whether the line terminator can be specified as third argument).
When reading fixed sized array you always need to make sure you do not read more than fits into this array! You may have heart about hackers exploiting software by using buffer overruns: assuming you'd actually indeed read a line first followed by splitting it into words you'd have created one of those exploitable programs! If you don't want to check before each word if there is enough space in the array, you'd use, e.g., a std::vector<std::string> (doing so also has a problem with hackers, namely that it opens up the program for a Denial of Service attack but although this is still a problem it is a somewhat lesser problem).
There are also a few smaller issues with your program, too:
If you are only reading from a string stream, you should use std::istringstream as there is no need to also set up the writing part of the std::stringstream.
The programs asks for "first name, middle name, and last name". I would read that specification to use, e.g., "John, F., Kennedy" but it seems you'd expect "John F. Kennedy". One reason I would expect that commas are to be used is that I don't have a middle name, i.e., I would enter "Dietmar, , Kühl".