C++ read specific range of line from file - c++

I have the following content in a file:
A(3#John Brook)
A(2#Allies Frank)
A(1#Lucas Feider)
I want to read the line piecemeal. First I want to read in order. For example, A than 3 than John Brook. Every thing is fine till 3 but how can I read John Brook without "#" and ")" as string.
I have a funciton and you can have a look my codes:
void readFile()
{
ifstream read;
char process;
char index;
string data;
read.open("datas.txt");
while(true)
{
read.get(process);
read.get(index);
// Here, I need to read "John Brook" for first line.
// "Allies Frank" for second line.
// "Lucas Feider" for third line.
}
read.close();
}

First organize your data into some structure.
struct Data {
char process;
char index;
std::string data;
};
Then implement function which is able to read single item. Read separators into temporary variables and then later check if they contain proper values.
Here is an example assuming each item is in single line.
std::istream& operator>>(std::istream& in, Data& d) {
std::string l;
if (std::getline(in, l)) {
std::istringstream in_line{l};
char openParan;
char separator;
if (!std::getline(
in_line >> d.process >> openParan >> d.index >> separator,
d.data, ')') ||
openParan != '(' || separator != '#') {
in.setstate(std::ios::failbit);
}
}
return in;
}
After that rest is quick and simple.
https://godbolt.org/z/aGYvPeWfW

Related

How to go the end of the current line in a file and check the character in c++?

In my project, I have two structs:
struct spell {
string name;
string effect;
};
struct spellbook {
string title;
string author;
int pages;
struct spell* s;
}
The spellbook and spell objects are arrays in this program. I'm reading information from a file and putting it into these two structs. An example of the file is:
2 //Number of spellbooks
Dark_Secrets Grady 357 //Title author pages
Evil_Force Dark //Spell name and effect
Grimoire Thompson 1967
Heavenly_Glow Light
Water_Ray Water
So the spellbook lines end with an integer (page number), while the spell lines end with a letter (The number at the top of the file is taken care of by another method). My plan for distinguishing between them was to check the last char in the line, and seeing if it was a digit. If it was, then I would add it to the spellbook struct. If not, then I would add it to the spell struct. As far as implementing this, I'm not sure how. Here is my add to spellbook struct method: (I have to have 2 different methods for this assignment)
void get_spellbook_data(spellbook* sb, int n, ifstream &file) //Int n is the number of spellbooks {
char end_line;
int i = 0;
//Jump to end of current line. I tried using file,seekg(-1, ios_base::end) but it got me to the end of the file, not the line
file.get(end_line); //Get last character in the line
//Jump back to the beginning of the current line
if (isdigit(end_line)) { //If current line is spellbook
file >> sb[i].title >> sb[i].author >> sb[i].pages;
i++;
}
else {//If its a spell line
//Skip this line and advance to the next one
}
}
That's my current idea so far. The method to add spells would basically be the same as this except checking if the current line does not end with a digit. I feel like there is a better method to get the information, but I'm not exactly sure of how to implement it.
Typical way to read a file line by line:
std::string line;
while (true) {
if (!std::getline(ifs, line)) {
// either eof or error, check ifs.eof()
// need to handle this somehow, leaving this up to you
}
// Now you have the whole text line in the line.
if (!line.empty()) break;
// the line was empty for some reason, continue to next line, until
// you get non-empty line, eof or error
}
// Now you have non-empty line. Check that the last char is digit...
if (std::isdigit(line.back()) {
// the last character is digit
}
// but I'd split line into space separated tokens and check if there is 2 or 3 tokens.
If I were you, I'd read a whole line in at a time, and then determine if I'm looking at a book or a spell line. So the code may look like:
struct spell {
std::string name;
std::string effect;
spell(std::string str) {
std::istringstream strm(str);
strm >> name >> effect;
}
};
struct spellbook {
std::string title;
std::string author;
int pages;
std::vector<spell> spells; // vector for nicer code and a cleaner name
spellbook(std::string str) {
std::istringstream strm(str);
strm >> title >> author >> pages;
}
};
bool is_book(std::string str) {
return isdigit(str.back());
}
int main() {
std::vector<spellbook> books;
ifstream fhand("Some/File/Location.txt");
std::string line;
while(std::getline(fhand, line)) {
if(is_book(line)) {
books.push_back(spellbook(line));
} else {
books.back().spells.push_back(spell(line));
}
}
}
Here's a live example (using stdin instead of a file): https://ideone.com/s9qk7Y

Ignoring empty line during fstream

Writing a program to read a text file and storing it in a struct. An example of the text file:
chicken
dog
car
765
When there is some text in the line, it will get store into the struct. I have tried the following:
getline(file, aLine);
Info.animalchicken = aLine;
getline(file, aLine);
Info.animaldog = aLine;
getline(file, aLine);
Info.car = aLine;
getline(file, aLine);
Info.number = aLine;
I realised that the getline is literally getting every single line. When I run this in my program, the chicken will be stored in the struct Info.animalchicken. The next line, which is empty, will store into Info.animaldog. Dog will be stored in Info.car and so on.
I think a control loop is required here but can't think of a good one. How can I ignore the empty line so my text can enter into the struct correctly?
This is my struct
struct Info {
string animalchicken;
string animaldog;
string car;
int number;
}
The loop idea, while quite primitive, should do the trick; the easiest way would be to wrap the logic in a separate function:
std::string getlineFilterEmpty(std::istream& s) {
std::string line;
do {
if (!s) {
throw std::runtime_error("End of stream");
}
getline(s, line);
} while(line.size() == 0);
return line;
}
Then getting your values is as simple as:
Info.animalchicken = getlineFilterEmpty(file);
Info.animaldog = getlineFilterEmpty(file);
Info.car = getlineFilterEmpty(file);
The number member will require parsing the string to an integer, the code for which you'll find elsewhere on SO.
The logic needs to go something like,
Read a line.
If read succeeded
If line not empty
Provide line
Else
Try again
Else
Handle error
Translating that into code and bundling it into a function for easy reuse, we get
std::string getNotEmptyLine(std::istream & in)
{
while (true) // repeat forever!
{
std::string temp;
std::getline(in, temp); // get a line
if (in) // test the line
{
if (line.size() != 0) // line not empty
{
return temp; //give it to caller
}
}
else
{
// handle error. We'll throw an exception, but this isn't the best solution
throw std::runtime_error("Couldn't read a line!");
}
}
}
As with all literal translations, it needs a bit of work. It would also be helpful to make this function work exactly like getline so the caller can use it as a drop-in replacement.
std::istream & getNotEmptyLine(std::istream & in, // stream to read
std::string & line, // somewhere to put the string
char delim = '\n') // allow different delimiters
{
while (true) // repeat forever!
{
if (std::getline(in, line, delim)) // get a line right in line and test that we got it.
{
if (line.size() != 0) // line not empty
{
break; // success. exit.
}
}
else
{
// line will contain whatever this implementation of `getline` puts or
// leaves in the string on failure.
break; // fail. Let the caller decide what to do
}
}
return in;
}
Usage:
Info info;
std::string aLine;
if (getNotEmptyLine(in, info.animalchicken) &&
getNotEmptyLine(in, info.animaldog) &&
getNotEmptyLine(in, info.car) &&
getNotEmptyLine(in, aLine))
{
info.number = std::stoi(aLine);
}
else
{
// handle error
}
Note: even this may be too simplistic. It can't handle a line that contains nothing but whitespace. A single misplaced and nigh-invisible space will wreak havoc. If this is a concern, add more logic to if (line.size() != 0)
Here's an option adding stream operators and a helper function to skip empty lines.
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
struct Info {
std::string animalchicken;
std::string animaldog;
std::string car;
int number;
};
// a helper function to do getline but skip empty lines
std::istream& getline_with_content(std::istream& is, std::string& s) {
while(std::getline(is, s)) if(not s.empty()) break;
return is;
}
// an istream operator to read one Info
std::istream& operator>>(std::istream& is, Info& i) {
getline_with_content(
getline_with_content(
getline_with_content(is,
i.animalchicken),
i.animaldog),
i.car);
is >> i.number;
// ignore everything after the number until a newline appears:
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return is;
}
// an ostream operator to print one Info
std::ostream& operator<<(std::ostream& os, const Info& i) {
return os << i.animalchicken << '\n'
<< i.animaldog << '\n'
<< i.car << '\n'
<< i.number << '\n';
}
int main() {
// an example istream with a lot of blank lines:
std::istringstream file(
"chicken\n\n"
"dog\n\n"
"car\n\n\n"
"765\n");
Info i;
file >> i; // read one Info from the stream
std::cout << i; // print one Info
}
Demo

How to read input from file and pair into a map in C++

I am trying to read through a text file that can possibly look like below.
HI bye
goodbye
foo bar
boy girl
one two three
I am trying to take the lines with only two words and store them in a map, the first word would be the key and second word would be the value.
below is the code I came up with but I can't figure out how to ignore the lines that do not have two words on them.
this only works properly if every line has two words. I understand why this is only working if every line has two words but, I'm not sure what condition I can add to prevent this.
pair myPair;
map myMap;
while(getline(file2, line, '\0'))
{
stringstream ss(line);
string word;
while(!ss.eof())
{
ss >> word;
myPair.first = word;
ss >> word;
myPair.second = word;
myMap.insert(myPair);
}
}
map<string, string>::iterator it=myMap.begin();
for(it=myMap.begin(); it != myMap.end(); it++)
{
cout<<it->first<<" "<<it->second<<endl;
}
Read two words into a temporary pair. If you can't, do not add the pair to the map. If you can read two words, see if you can read a third word. If you can, you have too many words on the line. Do not add.
Example:
while(getline(file2, line, '\0'))
{
stringstream ss(line);
pair<string,string> myPair;
string junk;
if (ss >> myPair.first >> myPair.second && !(ss >> junk))
{ // successfully read into pair, but not into a third junk variable
myMap.insert(myPair);
}
}
let me suggest a little different implementation
std::string line;
while (std::getline(infile, line)) {
// Vector of string to save tokens
vector <string> tokens;
// stringstream class check1
stringstream check1(line);
string intermediate;
// Tokenizing w.r.t. space ' '
while(getline(check1, intermediate, ' ')) {
tokens.push_back(intermediate);
}
if (tokens.size() == 2) {
// your condition of 2 words in a line apply
// process 1. and 2. item of vector here
}
}
You can use fscanf for take input from file and sscanf for take input from string with format. sscanf return how many input successfully take with given format. so you can easily check, how many word have a line.
#include<stdio.h>
#include<stdlib.h>
#include <iostream>
using namespace std;
int main()
{
char line[100];
FILE *fp = fopen("inp.txt", "r");
while(fscanf(fp, " %[^\n]s", line) == 1)
{
cout<<line<<endl;
char s1[100], s2[100];
int take = sscanf(line, "%s %s", s1, s2);
cout<<take<<endl;
}
return 0;
}

Reading in from a txt file. Trouble parsing info

I want to read in scores from a txt file. The scores are going into a struct.
struct playerScore
{
char name[32];
int score, difficulty;
float time;
};
the text file looks like this
Seth 26.255 40 7
as one line, where each item is followed by a tab. (Name\t time\t score\t difficulty\n)
When I begin to read in the text, I don't know how to tell the program when to stop. The scores file could be any number of lines or score entries. This is what I have attempted.
hs.open("scores.txt", ios_base::in);
hs.seekg(0, hs.beg);
if (hs.is_open())
{
int currpos = 0;
while (int(hs.tellg()) != int(hs.end));
{
hs>> inScore.name;
hs >> inScore.time;
hs >> inScore.score;
hs >> inScore.difficulty;
hs.ignore(INT_MAX, '\n');
AllScores.push_back(inScore);
currpos = (int)hs.tellg();
}
}
I'm trying to make a loop that will read in a line of code into a temp struct for the data, then push that struct into a vector of structs. Then update the currpos variable with the current location of the input pointer. However, the loop just gets stuck on the condition and freezes.
There are a multitude of ways to do this, but the following is likely what you're looking for. Declare a free-operator for extracting a single-line definition of a player-score:
std::istream& operator >>(std::istream& inf, playerScore& ps)
{
// read a single line.
std::string line;
if (std::getline(inf, line))
{
// use a string stream to parse line by line.
std::istringstream iss(line);
if (!(iss.getline(ps.name, sizeof(ps.name)/sizeof(*ps.name), '\t') &&
(iss >> ps.time >> ps.score >> ps.difficulty)))
{
// fails to parse a full record. set the top-stream fail-bit.
inf.setstate(std::ios::failbit);
}
}
return inf;
}
With that, your read code can now do this:
std::istream_iterator<playerScore> hs_it(hs), hs_eof;
std::vector<playerScore> scores(hs_it, hs_eof);
I dont think that you can just >> from your file. Do you think it will take everything till \t? :)
You can try to take for example token with strtok()
I guess it can use '\t' to split string and take for each variable via this function needed part of string
In case if it strtok() doesnt work that way i guess you can just copy till '\t' in sub-loop
You can do like this
playerScore s1;
fstream file;
file.open("scores.txt", ios::in | ios::out);
while(!file.eof()) //For end of while loop
{
file.read(s1, sizeof(playerScore));//read data in one structure.
AllScores.push_back(s1);
}

Tokenize stringstream based on type

I have an input stream containing integers and special meaning characters '#'. It looks as follows:
... 12 18 16 # 22 24 26 15 # 17 # 32 35 33 ...
The tokens are separated by space. There's no pattern for the position of '#'.
I was trying to tokenize the input stream like this:
int value;
std::ifstream input("data");
if (input.good()) {
string line;
while(getline(data, line) != EOF) {
if (!line.empty()) {
sstream ss(line);
while (ss >> value) {
//process value ...
}
}
}
}
The problem with this code is that the processing stops when the first '#' is encountered.
The only solution I can think of is to extract each individual token into a string (not '#') and use atoi() function to convert the string to an integer. However, it's very inefficient as the majority tokens are integer. Calling atoi() on the tokens introduces big overhead.
Is there a way I can parse the individual token by its type? ie, for integers, parse it as integers while for '#', skip it. Thanks!
One possibility would be to explicitly skip whitespace (ss >> std::ws), and then to use ss.peek() to find out if a # follows. If yes, use ss.get() to read it and continue, otherwise use ss >> value to read the value.
If the positions of # don't matter, you could also remove all '#' from the line before initializing the stringstream with it.
Usually not worth testing against good()
if (input.good()) {
Unless your next operation is generating an error message or exception. If it is not good all further operations will fail anyway.
Don't test against EOF.
while(getline(data, line) != EOF) {
The result of std::getline() is not an integer. It is a reference to the input stream. The input stream is convertible to a bool like object that can be used in bool a context (like while if etc..). So what you want to do:
while(getline(data, line)) {
I am not sure I would read a line. You could just read a word (since the input is space separated). Using the >> operator on string
std::string word;
while(data >> word) { // reads one space separated word
Now you can test the word to see if it is your special character:
if (word[0] == "#")
If not convert the word into a number.
This is what I would do:
// define a class that will read either value from a stream
class MyValue
{
public:
bool isSpec() const {return isSpecial;}
int value() const {return intValue;}
friend std::istream& operator>>(std::istream& stream, MyValue& data)
{
std::string item;
stream >> item;
if (item[0] == '#') {
data.isSpecial = true;
} else
{ data.isSpecial = false;
data.intValue = atoi(&item[0]);
}
return stream;
}
private:
bool isSpecial;
int intValue;
};
// Now your loop becomes:
MyValue val;
while(file >> val)
{
if (val.isSpec()) { /* Special processing */ }
else { /* We have an integer */ }
}
Maybe you can read all values as std::string and then check if it's "#" or not (and if not - convert to int)
int value;
std::ifstream input("data");
if (input.good()) {
string line;
std::sstream ss(std::stringstream::in | std::stringstream::out);
std::sstream ss2(std::stringstream::in | std::stringstream::out);
while(getline(data, line, '#') {
ss << line;
while(getline(ss, line, ' ') {
ss2 << line;
ss2 >> value
//process values ...
ss2.str("");
}
ss.str("");
}
}
In here we first split the line by the token '#' in the first while loop then in the second while loop we split the line by ' '.
Personally, if your separator is always going to be space regardless of what follows, I'd recommend you just take the input as string and parse from there. That way, you can take the string, see if it's a number or a # and whatnot.
I think you should re-examine your premise that "Calling atoi() on the tokens introduces big overhead-"
There is no magic to std::cin >> val. Under the hood, it ends up calling (something very similar to) atoi.
If your tokens are huge, there might be some overhead to creating a std::string but as you say, the vast majority are numbers (and the rest are #'s) so they should mostly be short.