Reading In Formatted Data with Multiple Delimiters C++ - c++

I'm trying to read in data from command prompt with multiple delimiters, for example:
data 1, data2, data3.
I'd like the code to read in the data before the first comma, the data after that and before the second comma, and finally the data after that but before the period. I have been using this:
getline(cin, VAR, ',');
cin.get();
getline(cin, VAR2, ' ');
getline(cin, VAR3, ',');
cin.get();
getline(cin, VAR4, '.');
And it does what I want, provided the information is entered correctly. However, how can I check and see if the information wasn't? Because if the user doesn't enter two commas and a period, the program gets stuck as I'm assuming the cin stream gets broken after not finding the delimiter as specified in the getline() read. I've tried this:
if (cin.fail()) {
cout << "failure" << endl;
}
After a getline, but it never runs if the delimiter isn't input. What's the best way to do this, check for errors and see if data wasn't entered in the desired format?

You have to parse the input yourself.
For starters, your input does not consist of "data with multiple delimiters". Your input consists of a single line of text, that was entered, according to your description, at a command prompt of some kind.
If so, then the line of text should be read with a single std::getline() library function, since, by default, std::getline() uses \n as a default delimiter.
std::string str;
std::getline(std::cin, str);
Now, you've read your input into str. Now that this task is complete you can get down to business verifying whether str contains properly formatted input with the appropriate delimiters, or not.
To do that, you will use the rich set of methods that are available from your std::string object. For example one such method, find(), searches the string for a particular character, such as your first comma:
auto n=str.find(',');
The full documentation for find(), whose description you will find in your C++ book, will explain how to determine whether find() actually found the comma, or not. Based on that your program can either take the appropriate action (if the comma was not found) or use the substr() method, on this str, to extract the part of the string before the comma. That would be your first VAR. Then, use substr() again to extract the rest of the entered text, after the first comma.
You will also find a complete description of substr() in your C++ book.
After using substr() to extract the rest of the entered text, after the first comma, you will simply repeat the same overall process for extracting data2, and the following item of input, this time using a period.
And, at each step of the way you will have all the information your program will need to determine whether or not your input was formatted correctly.

You could read in the entire line into a std::string, then use std::istringstream to parse the string:
#include <iostream>
#include <string>
#include <sstream>
#include <cstdlib>
int main(void)
{
const std::string input_text = "data1, data2, data3\n";
std::istringstream input_stream(input_text);
std::string data1;
std::string data2;
std::string data3;
std::getline(input_stream, data1, ',');
std::getline(input_stream, data2, ',');
std::getline(input_stream, data3, ',');
std::cout << "data1: " << data1 << '\n';
std::cout << "data2: " << data2 << '\n';
std::cout << "data3: " << data3 << '\n';
return EXIT_SUCCESS;
}
If the std::getline fails, that means there is an error in the data.

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.

In c++, how do you get the input of a string, float and integer from 1 line?

An input file is entered with the following data:
Juan Dela Cruz 150.50 5
'Juan Dela Cruz' is a name that I would like to assign to string A,
'150.50' is a number I would like to assign to float B
and 5 is a number I would like to assign to int C.
If I try cin, it is delimited by the spaces in between.
If I use getline, it's getting the whole line as a string.
What would be the correct syntax for this?
If we analyze the string, then we can make the following observation. At the very end, we have an integer. In front of the integer we have a space. And in front of that the float value. And again in fron of that a space.
So, we can simply look from the back of the string for the 2nd last space. This can easily be achieved by
size_t position = lineFromeFile.rfind(' ', lineFromeFile.rfind(' ')-1);
We need a nested statement of rfind please see here, version no 3.
Then we build a substring with the name. From start of the string up to the found position.
For the numbers, we put the rest of the original string into an std::istringstream and then simply extract from there.
Please see the following simple code, which has just a few lines of code.
#include <iostream>
#include <string>
#include <cctype>
#include <sstream>
int main() {
// This is the string that we read via getline or whatever
std::string lineFromeFile("Juan Dela Cruz 150.50 5");
// Let's search for the 2nd last space
size_t position = lineFromeFile.rfind(' ', lineFromeFile.rfind(' ')-1);
// Get the name as a substring from the original string
std::string name = lineFromeFile.substr(0, position);
// Put the numbers in a istringstream for better extraction
std::istringstream iss(lineFromeFile.substr(position));
// Get the rest of the values
float fValue;
int iValue;
iss >> fValue >> iValue;
// Show result to use
std::cout << "\nName:\t" << name << "\nFloat:\t" << fValue << "\nInt:\t" << iValue << '\n';
return 0;
}
Probably simplest in this case would be to read whole line into string and then parse it with regex:
const std::regex reg("\\s*(\\S.*)\\s+(\\d+(\\.\\d+)?)\\s+(\\d+)\\s*");
std::smatch match;
if (std::regex_match( input, match, reg)) {
auto A = match[1];
auto B = std::stof( match[2] );
auto C = std::stoi( match[4] );
} else {
// error invalid format
}
Live example
As always when the input does not (or sometimes does not) match a strict enough syntax, read the whole line and then apply the rules which to a human are "obvious".
In this case (quoting comment by john):
Read the whole string as a single line. Then analyze the string to work out where the breaks are between A, B and C. Then convert each part to the type you require.
Specifically, you probably want to use reverse searching functions (e.g. https://en.cppreference.com/w/cpp/string/byte/strrchr ), because the last parts of the input seem the most strictly formatted, i.e. easiest to parse. The rest is then the unpredictable part at the start.
either try inputting the different data type in different lines and then use line breaks to input different data types or use the distinction to differentiate different data types like adding a . or comma
use the same symbol after each data package, for example, Juan Dela Cruz;150.50;5 then you can check for a ; and separate your string there.
If you want to use the same input format you could use digits as an indicator to separate them

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

Building a list of words from a sentence inputted

I am fairly new to programming and would like help with my homework. I have no idea where to even start.
"
1. Have the user input a sentence
2. Print out the individual words in the sentence, along with the word number
So the string "This is a test of our program." should produce:
1. This
2. is
3. a
4. test
5. of
6. our
7. program
This should strip out all spaces, commas, periods, exclamation points."
if you can give me some pointers. thanks.
You will have to use strings and streams from the standard library. You can start by including the following headers
#include <string>
#include <iostream>
A good starting point would be to look at the introduction here
Try some stuff with std::cout. This method allows you to output content to the console. Start with something easy, such as:
std::cout << "Hello World" << endl;
You can also output the content of a variable the same way:
std::string myString = "SomeText";
std::cout << myString << endl;
std::cout does the opposite. It allows you to capture the user input into a variable.
int myNumber;
std::cin >> myNumber;
or
std::string userInputString;
std::getline(std::cin, userInputString)
Notice that in the second case we're using std::getline. This is because std::cin behaves in such a way that it will stop after the first word if you write an entire sentence.
Now that you've captured the user input string, you can remove undesired characters, split the string, etc.. Look at what is available in the string class. Good luck.

ifstream get line change output from char to string

C++ ifstream get line change getline output from char to string
I got a text file.. so i read it and i do something like
char data[50];
readFile.open(filename.c_str());
while(readFile.good())
{
readFile.getline(data,50,',');
cout << data << endl;
}
My question is instead of creating a char with size 50 by the variable name data, can i get the getline to a string instead something like
string myData;
readFile.getline(myData,',');
My text file is something like this
Line2D, [3,2]
Line3D, [7,2,3]
I tried and the compiler say..
no matching function for getline(std::string&,char)
so is it possible to still break by delimiter, assign value to a string instead of a char.
Updates:
Using
while (std::getline(readFile, line))
{
std::cout << line << std::endl;
}
IT read line by line, but i wanna break the string into several delimiter, originally if using char i will specify the delimiter as the 3rd element which is
readFile.getline(data,50,',');
how do i do with string if i break /explode with delimiter comma , the one above. in line by line
Use std::getline():
std::string line;
while (std::getline(readFile, line, ','))
{
std::cout << line << std::endl;
}
Always check the result of read operations immediately otherwise the code will attempt to process the result of a failed read, as is the case with the posted code.
Though it is possible to specify a different delimiter in getline() it could mistakenly process two invalid lines as a single valid line. Recommend retrieving each line in full and then split the line. A useful utility for splitting lines is boost::split().