I'm trying to parse a file that has the following information
test_wall ; Comments!!
je,5
forward
goto,1
test_random;
je,9
I'm supposed to ignore comments after the ";" and move on to the next line. When there is a comma I'm trying to ignore the comma and store the second value.
string c;
int a;
c=getChar();
ifstream myfile (filename);
if (myfile.is_open())
{
while ( c != ';' && c != EOF)
{
c = getchar();
if( c == ',')
{
a= getChar();
}
}
}
myfile.close();
}
Here's some code. I'm not entirely sure I've understood the problem correctly, but if not hopefully this will set you on the right direction.
ifstream myfile (filename);
if (myfile.is_open())
{
// read one line at a time until EOF
string line;
while (getline(myFile, line))
{
// does the line have a semi-colon?
size_t pos = line.find(';');
if (pos != string::npos)
{
// remove the semi-colon and everything afterwards
line = line.substr(0, pos);
}
// does the line have a comma?
pos = line.find(',');
if (pos != string::npos)
{
// get everything after the comma
line = line.substr(pos + 1);
// store the string
...
}
}
}
I've left the section commented 'store the string' blank because I'm not certain what you want to do here. Possibly you are asking to convert the string into an integer before storing it. If so then add that code, or ask if you don't know how to do that. Actually don't ask, search on stack overflow, because that question has been asked hundreds of times.
Related
Currently learning c++ and I'm pretty stumped. I want to count the instances of a character in a text file - but not including lines that start with a certain character. Specifically, I'm counting instances of Gs and Cs in a text file, but not including lines that begin with "*"
Example
*metadata information
atgctaatgcaggtcagtcagtcagtcatgcg
atgcagtcagtcactgactgactgactgaata
*metadata information
atgtagcagctagtcagtcagtcagcatatat
gatcgactagctgactgacgtactgactgaat
char Z;
long GC=0;
string Line;
while(getline(InFile, Line))
{
if(Line[0]=='*')
{
InFile.get(Z);
while(InFile.get(Z))
{
if(Z=='G' || Z=='C' || Z=='g' || Z=='c')
{
++GC;
}
}
}
}
I'm able to count the instances of g and c across the entire text, but just haven't been able to limit the function to lines that do not begin in >
My understanding of your requirements, you want to ignore lines starting with '*'.
while (getline(InFile, Line))
{
if (Line[0] == '*')
{
continue; // ignore the line
}
for (int i = 0; i < Line.length(); ++i)
{
const char c = std::toupper(Line[i]);
if ((c == 'G') || (c == 'C`))
{
++GC;
}
}
}
In the above code, if the first line character is '*', the line is ignored.
Otherwise, the string is searched for 'G' or 'C' characters.
InFile.get(Z);
while(InFile.get(Z))
You don't want those lines. At this point in your code, the whole string has already been read in string Line;
You probably want
for(auto c: Line) // go over every char in Line
{
And you probably want to fix:
if(Line[0] != '*')
because
but not including lines that start with a certain character.
I am learning c++ so bear with me and apologize for any idiocy beforehand.
I am trying to write some code that matches the first word on each line in a file called "command.txt" to either "num_lines", "num_words", or "num_chars".
If the first word of the first line does not match the previously mentioned words, it reads the next line.
Once it hits a matching word (first words only!) it prints out the matching word.
Here is all of my code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
ifstream comm_in("commands.txt"); // opens file
string command_name = "hi"; // stores command from file
bool is_command() {
if (command_name == "num_words" || command_name == "num_chars" || command_name == "num_lines") {
return true;
} else {
return false;
}
}
// FIND a first word of a line in file THAT MATCHES "num_words", "num_chars" or "num_lines"
void get_command() {
string line;
char c;
while (!is_command()) { // if command_name does not match a command
// GET NEXT LINE OF FILE TO STRING
getline(comm_in, line);
// SUPPOSED TO GET THE FIRST WORD OF A STRING (CANT USE SSTREAM)
for (int i = 0; i < line.size(); i++) { // increment through line
c = line[i]; // assign c as index value of line
if (c == ' ' || c == '\t') { // if c is a space/tab
break; // end for loop
} else {
command_name += c; // concatenate c to command_name
} // if
} // for
} // while
return;
}
int main() {
get_command();
cout << command_name; // supposed to print "num_lines"
}
The contents of the command.txt file:
my bear is happy
and that it
great ha
num_lines sigh
It compiles properly, but when I run it in my terminal, nothing shows up; it doesn't seem to ever stop loading.
How can I fix this?
Unless you really want to hate yourself in the morning (so to speak) you want to get out of the habit of using global variables. You'll also almost certainly find life easier if you break get_command into (at least) two functions, one specifically to get the first word from the string containing the line.
I'd write the code more like this:
bool is_cmd(std::string const &s) {
return s == "num_words" || s == "num_chars" || s == "num_lines";
}
std::string first_word(std::istream &is) {
std::string line, ret;
if (std::getline(is, line)) {
auto start = line.find_first_not_of(" \t");
auto end = line.find_first_of(" \t", start);
ret = line.substr(start, end - start);
}
return ret;
}
void get_command(std::istream &is) {
std::string cmd;
while (!(cmd = first_word(is)).empty())
if (is_cmd(cmd)) {
std::cout << cmd;
break;
}
}
This still isn't perfect (e.g., badly formed input could still cause it to fail) but at least it's a move in what I'd say is a better direction.
If something goes wrong and you reach the end of file the loop will never stop. You should change getline(comm_in, line) to if(!getline(comm_in, line)) break;, or better yet, use that as the condition for the loop.
You also have to reset command_name for each pass:
while(getline(comm_in, line))
{
command_name = "";
for(int i = 0; i < line.size(); i++)
{
c = line[i];
if(c == ' ' || c == '\t')
break;
else
command_name += c;
}
if(is_command())
break;
}
// FIND a first word of a line in file THAT MATCHES "num_words", "num_chars" or "num_lines"
void get_command()
{
string line;
char c;
while (!is_command()) { // if command_name does not match a command
// GET NEXT LINE OF FILE TO STRING
if(getline(comm_in, line),comm_in.fail()){
// end reading
break;
}
//clear
command_name = "";
// SUPPOSED TO GET THE FIRST WORD OF A STRING (CANT USE SSTREAM)
for (int i = 0; i < line.size(); i++) { // increment through line
c = line[i]; // assign c as index value of line
if (c == ' ' || c == '\t') { // if c is a space/tab
break; // end for loop
} else {
command_name += c; // concatenate c to command_name
} // if
} // for
} // while
return;
}
The key of this problem is that you didn't clear the command_name.
What's more, you have to add a judge about whether reaching the end of the file.
ps: if(getline(comm_in, line),comm_in.fail()) is equal to if(getline(comm_in, line)),
I will keep it short and simple. After making sure that user is able to open a file succesfully, I have written the following piece of code to take a line from the inputFile.
string line;
int counter = 0;
DynIntStack stack;
while (!inputFile.eof())
{
getline(inputFile, line);
stringstream inputLine(line);
counter++;
//I NEED TO DO IT HERE
}
This will be used to write program to check balanced paranthesis in an input cpp file and I have to use stacks. Classic CS homework as I understand from the topics I have checked :)
counter is updated after every line and the line number(counter) is to be pushed to the stack if it has a opening bracket and it must be popped from the stack if it is a closing bracket. after these, the output should look something like this:
block: 3 - 3
block: 12 - 14
block: 10 - 14
block: 5 - 16
Syntax error in line 21.
But I do not know how to check the line I got char by char. I need a loop to check the chars and apply the previously mentioned things if an opening or closing bracket is found. How can I check the line char by char.
using any data container other than stacks is forbidden.
thank you very much :)
But I do not know how to check the line I got char by char
Is this what you want?
string line;
int counter = 0;
DynIntStack stack;
while (getline(inputFile, line))
{
counter++;
for(size_t i = 0; i < line.length(); i++) {
// line[i] is i'th character
if(line[i] == '(') {
// do stuff
}
else if(line[i] == ')') {
// do stuff
}
}
}
In addition to the correct answer by Kaidul Islam, a std::string support range based for loops.
string line;
int counter = 0;
DynIntStack stack;
while (getline(inputFile, line))
{
++counter;
for (char const c : line)
{
if (c == '(')
{
// do stuff
}
else if (c == ')')
{
// do stuff
}
}
}
So I've seen lots of solutions on this site and tutorials about reading in from a text file in C++, but have yet to figure out a solution to my problem. I'm new at C++ so I think I'm having trouble piecing together some of the documentation to make sense of it all.
What I am trying to do is read a text file numbers while ignoring comments in the file that are denoted by "#". So an example file would look like:
#here is my comment
20 30 40 50
#this is my last comment
60 70 80 90
My code can read numbers fine when there aren't any comments, but I don't understand parsing the stream well enough to ignore the comments. Its kind of a hack solution right now.
/////////////////////// Read the file ///////////////////////
std::string line;
if (input_file.is_open())
{
//While we can still read the file
while (std::getline(input_file, line))
{
std::istringstream iss(line);
float num; // The number in the line
//while the iss is a number
while ((iss >> num))
{
//look at the number
}
}
}
else
{
std::cout << "Unable to open file";
}
/////////////////////// done reading file /////////////////
Is there a way I can incorporate comment handling with this solution or do I need a different approach? Any advice would be great, thanks.
If your file contains # always in the first column, then just test, if the line starts with # like this:
while (std::getline(input_file, line))
{
if (line[0] != "#" )
{
std::istringstream iss(line);
float num; // The number in the line
//while the iss is a number
while ((iss >> num))
{
//look at the number
}
}
}
It is wise though to trim the line of leading and trailing whitespaces, like shown here for example: Remove spaces from std::string in C++
If this is just a one of use, for line oriented input like yours, the
simplest solution is just to strip the comment from the line you just
read:
line.erase( std::find( line.begin(), line.end(), '#' ), line.end() );
A more generic solution would be to use a filtering streambuf, something
like:
class FilterCommentsStreambuf : public std::streambuf
{
std::istream& myOwner;
std::streambuf* mySource;
char myCommentChar;
char myBuffer;
protected:
int underflow()
{
int const eof = std::traits_type::eof();
int results = mySource->sbumpc();
if ( results == myCommentChar ) {
while ( results != eof && results != '\n') {
results = mySource->sbumpc(0;
}
}
if ( results != eof ) {
myBuffer = results;
setg( &myBuffer, &myBuffer, &myBuffer + 1 );
}
return results;
}
public:
FilterCommentsStreambuf( std::istream& source,
char comment = '#' )
: myOwner( source )
, mySource( source.rdbuf() )
, myCommentChar( comment )
{
myOwner.rdbuf( this );
}
~FilterCommentsStreambuf()
{
myOwner.rdbuf( mySource );
}
};
In this case, you could even forgo getline:
FilterCommentsStreambuf filter( input_file );
double num;
while ( input_file >> num || !input_file.eof() ) {
if ( ! input_file ) {
// Formatting error, output error message, clear the
// error, and resynchronize the input---probably by
// ignore'ing until end of line.
} else {
// Do something with the number...
}
}
(In such cases, I've found it useful to also track the line number in
the FilterCommentsStreambuf. That way you have it for error
messages.)
An alternative to the "read aline and parse it as a string", can be use the stream itself as the incoming buffer:
while(input_file)
{
int n = 0;
char c;
input_file >> c; // will skip spaces ad read the first non-blank
if(c == '#')
{
while(c!='\n' && input_file) input_file.get(c);
continue; //may be not soooo beautiful, but does not introduce useless dynamic memory
}
//c is part of something else but comment, so give it back to parse it as number
input_file.unget(); //< this is what all the fuss is about!
if(input_file >> n)
{
// look at the nunber
continue;
}
// something else, but not an integer is there ....
// if you cannot recover the lopop will exit
}
This is my partial code:
if(action=="auth")
{
myfile.open("account.txt");
while(!myfile.eof())
{
getline(myfile,sline);
vector<string> y = split(sline, ':');
logincheck = "";
logincheck = y[0] + ":" + y[3];
if (sline==actionvalue)
{
sendClient = "login done#Successfully Login.";
break;
}
else
{
sendClient = "fail login#Invalid username/password.";
}
y.clear();
}
myfile.close();
}
If i don't have this
logincheck = y[0] + ":" + y[3];
The code will not have any segmentation core dump error, but when I add that line, it will went totally wrong.
My account.txt is as followed:
admin:PeterSmite:hr:password
cktang:TangCK:normal:password
The split function:
std::vector<std::string> split(std::string const& str, std::string const& delimiters = "#") {
std::vector<std::string> tokens;
// Skip delimiters at beginning.
string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first "non-delimiter".
string::size_type pos = str.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos) {
// Found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = str.find_first_of(delimiters, lastPos);
}
return tokens;
}
std::vector<std::string> split(std::string const& str, char const delimiter) {
return split(str,std::string(1,delimiter));
}
You should do some basic input checking before you blithely assume that the vector contains at least 4 elements, otherwise y[3] will explode when you parse a line of input without three colons:
if (y.size >= 4) {
// Do login check
} else {
// Invalid input
}
I'd guess that you probably have a blank line in your input.
Wrap the whole section of code that relies on reading a "a:b:c:d" line of input:
if(action=="auth") {
myfile.open("account.txt");
while(getline(myfile,sline))
{
vector<string> y = split(sline, ':');
if (y.size >= 4) {
logincheck = "";
logincheck = y[0] + ":" + y[3];
if (sline==actionvalue) {
sendClient = "login done#Successfully Login.";
break;
} else {
sendClient = "fail login#Invalid username/password.";
}
}
}
myfile.close();
}
The problem is the structure of your loop:
while(!myfile.eof())
{
getline(myfile,sline);
istream::eof() isn't guaranteed to return true until you attempt to read past the end of the stream. So what happens is you read 2 lines and eof() still hasn't return true. Then you enter the loop for the 3rd time. Since you don't check for errors after getline call, you happily access sline when its content is unspecified - it could be empty, it could still carry the content from the previous iteration, it could contain something else.
You always need to check if getline() call is succesful before you attempt to access the string. The idiomatic way is to put it in the condition of the loop:
while (getline(myfile, sline)) { /* do your stuff */ }
This way you only enter the loop body if the read is successful.
The problem is that the call to getline that pulls the last usable line isn't setting EOF, so you do one extra loop iteration after you have gotten the last usable line. That loop operation is running on an empty sline, which causes bad things to happen, namely, split doesn't return a vector with four elements, but then you try to access those elements.
You can just use
while (getline(myfile,sline))
{
// do stuff
}
in place of
while(!myfile.eof())
{
getline(myfile,sline);
// do stuff
}