I'm getting this error on my program and I don't understand why. The code essentially has to check for tags stored in a set which is declared as a global variable. If it's a valid tag it stores it in the stack if not returns a error message. Then it checks (if its a valid tag)if the closing tags are in order. This is all for the is_well_formed method. For the print_well_formed_file method it essentially checks if the given file is well formed if it is it'll display the file.:
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::substr
What can I do to fix this error ?
This is part of the code:
bool is_well_formed(ifstream& ifs, string& error_msg) {
// your code goes here
string fname, line;
Token tok;
Lexer lexer;
tags.insert("blue");
tags.insert("red");
tags.insert("cyan");
tags.insert("white");
tags.insert("yellow");
tags.insert("magenta");
tags.insert("dim");
tags.insert("underline");
tags.insert("bold");
while (getline(cin, fname)) {
// tries to open the file whose name is in string fname
string name = fname.substr(1, fname.length() - 2);
cout << "Name" + name;
ifs.open(name.c_str());
if (ifs.fail()) {
cerr << "ERROR: Failed to open file " << fname << endl;
ifs.clear();
} else {
while (getline(ifs, line)) {
lexer.set_input(line);
while (lexer.has_more_token()) {
tok = lexer.next_token();
string tmpTok = tok.value;
switch (tok.type) {
case TAG:
// If it has /, remove / from tmpTok
if (tok.value[0] == '/') {
tmpTok = tmpTok.substr(1, tmpTok.length() - 1);
}
if (tags.find(tmpTok) == tags.end()) {
// Check whether the encountered tag is valid
error_return("Tag " + tmpTok + " is invalid!");
} else {
// Valid Tag encountered
stack < string > tagstack;
tagstack.push(tmpTok);
// Check if the tags are formed properly
if (tok.value[0] == '/') {
// Remove / from tmpTok
string closingTag = tmpTok;
string openingTag = tagstack.top();
tagstack.pop();
if (closingTag.compare(openingTag) != 0) {
error_return(
closingTag + "doesn't match"
+ openingTag);
} //else
// return true; // if the file is well formed
}
}
break;
case IDENT:
cout << "IDENT: " << tok.value << endl;
break;
case ERRTOK:
error_return("Syntax error on this line\n");
//cout << "Syntax error on this line\n";
break;
case ENDTOK:
break;
}
}
}
}
}
return true; // if the file is well-formed
}
void print_well_formed_file(ifstream& ifs) {
//Check if file is well formed.
string line;
Lexer command;
if (is_well_formed(ifs, line)) { //if well formed display
command.set_input(line);
display(command);
}
}
void display(Lexer cmd_lexer) {
string file_name;
if (!parse_input(cmd_lexer, file_name)) {
error_return("Syntax error: display <filename>");
return;
}
ifstream ifs(file_name.c_str());
string error_msg;
if (ifs) {
if (!is_well_formed(ifs, error_msg)) {
error_return(error_msg);
} else {
ifs.clear(); // clear EOF flag
ifs.seekg(0, ios::beg); // go back to the very beginning
print_well_formed_file(ifs);
}
} else {
error_return("Can't open " + file_name + " for reading");
}
ifs.close();
}
Example of user input:
validate <file name>
display <file name>
exit
string name = fname.substr(1, fname.length() - 2);
Will throw this kind of an exception if fname's length is <= 1 . I bet, this is the case. The simplest(not the best) solution is to skip such lines.
Related
I have an application that must monitor some folders (in Windows) to detect if a file was created in that folder (real use is to detect incoming FTP files).
If a file is detected , it is read, then deleted .
Occasionally, I get a file reading error on a file that was detected.
Question is: Why?
To simulate the error, I created a simple program to reproduce it:
std::vector<std::filesystem::path> watch;
void main()
{
watch.push_back("D:\\test1"); //must exist
watch.push_back("D:\\test2");
watch_dir();
}
this example monitors 2 folders.
To simulate incoming files on the folder, another program copies files to that folder
continuously at configurable intervals (say 100 milliseconds).
To detect folder changes , WIN32 API functions FindFirstChangeNotification and WaitForMultipleObjects are used, based on this Microsoft example
https://learn.microsoft.com/en-us/windows/win32/fileio/obtaining-directory-change-notifications
detection function adapted from the example (Note: WaitForMultipleObjects blocks until a change is detected)
void watch_dir()
{
HANDLE handle[2];
memset(handle, 0, 2 * sizeof(HANDLE));
for (size_t idx = 0; idx < watch.size(); idx++)
{
std::string str = watch.at(idx).string();
LPTSTR path = (LPTSTR)str.c_str();
std::cout << "watch path " << path << std::endl;
handle[idx] = FindFirstChangeNotification(
path, // directory to watch
FALSE, // do not watch subtree
FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes
if (handle[idx] == INVALID_HANDLE_VALUE)
{
assert(0);
ExitProcess(GetLastError());
}
}
while (TRUE)
{
std::cout << "Waiting for notification..." << std::endl;
DWORD wait_status = WaitForMultipleObjects(watch.size(), handle, FALSE, INFINITE);
std::cout << "Directory " << watch.at(wait_status) << " changed" << std::endl;
if (FindNextChangeNotification(handle[wait_status]) == FALSE)
{
assert(0);
ExitProcess(GetLastError());
}
std::filesystem::path path = watch.at(wait_status);
send_files_in_path(path);
}
}
Once a change is detected by the function above, then all files in the folder are listed
and read, by these functions
void send_files_in_path(const std::filesystem::path& ftp_path)
{
std::vector<std::filesystem::path> list = get_files(ftp_path);
for (size_t idx = 0; idx < list.size(); idx++)
{
std::string buf;
read_file(list.at(idx).string(), buf);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::filesystem::remove(list.at(idx));
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//get_files
//get all ".txt" files inside a FTP folder
/////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<std::filesystem::path> get_files(const std::filesystem::path& base_archive_path)
{
std::vector<std::filesystem::path> list;
try
{
for (const auto& entry : std::filesystem::recursive_directory_iterator(base_archive_path))
{
std::filesystem::path path = entry.path();
if (!entry.is_regular_file())
{
continue;
}
std::string fname = entry.path().filename().string();
size_t len = fname.size();
size_t pos = len - 4;
//check if last 4 characters are ".txt"
if (fname.find(".txt", pos) == std::string::npos && fname.find(".TXT", pos) == std::string::npos)
{
continue;
}
SPDLOG_INFO("loading: " + entry.path().string());
list.push_back(path);
}//this path
} //try
catch (const std::exception& e)
{
SPDLOG_ERROR(e.what());
}
return list;
}
The function where the error happens is
int read_file(const std::string& fname, std::string& buf)
{
std::ifstream ifs;
std::ios_base::iostate mask = ifs.exceptions() | std::ios::failbit;
ifs.exceptions(mask);
std::this_thread::sleep_for(std::chrono::milliseconds(0));
std::cout << "opening : " << fname << std::endl;
try
{
ifs.open(fname);
if (!ifs.is_open())
{
std::cout << "open fail: " << fname << std::endl;
return -1;
}
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
return -1;
}
std::stringstream ss;
ss << ifs.rdbuf();
ifs.close();
buf = ss.str();
return 0;
}
the try/catch block, again, occasionally , is triggered with the error
ios_base::failbit set: iostream stream error
removing the try/catch block, and the open mask (just to try), then
ifs.is_open
fails.
A temporary solution was to detect the cases where the open() failed and repeat it.. which succeeds, because the file does exist.
Calling this with a small delay before the open call has the effect of reducing the open fails
std::this_thread::sleep_for(std::chrono::milliseconds(10));
ifs.open(fname);
But still would like to find out the reason for the occasional failure
-----update-----
I managed to fix the issue by not exiting mid-function but returning to main and exiting there.
Can you explain why it worked?
tnx
I keep getting still reachable valgrind error on my code.
Already tried:
close file before exit, and before function readCSV ends
declare iss once, and using iss.str, iss.clear
(all destructors are default)
I think this function is causing me problems. I's called from the main function, and reads the csv file into a deque:
static std::deque<Fractal *> readCSV(const std::string &path)
{
std::deque<Fractal *> fractals;
std::ifstream ifs(path);
if (ifs.fail())
{
std::cerr << INV_INPUT << std::endl;
exit(1);
}
std::string line;
while (getline(ifs, line))
{
std::istringstream iss(line);
std::string arg;
int type, height;
getline(iss, arg, ',');
type = strToN(arg);//parse int from string, -1 if not a valid integer
getline(iss, arg, ',');
height = strToN(arg);
if (height <= 0 || height > MAX_HEIGHT)
{
std::cerr << INV_INPUT << std::endl;
deleteFractalArr(fractals);
exit(1);
}
if (getline(iss, arg, ','))
{
std::cerr << INV_INPUT << std::endl;
deleteFractalArr(fractals);
exit(1);
}
switch (type)
{
case 1:
fractals.push_front(new SCarpet(height));
break;
case 2:
fractals.push_front(new SSieve(height));
break;
case 3:
fractals.push_front(new CDust(height));
break;
default:
std::cerr << INV_INPUT << std::endl;
deleteFractalArr(fractals);
exit(1);
}
}
return fractals;
}
the deleteFractalArr is as follows:
static void deleteFractalArr(const std::deque<Fractal *> &fractals)
{
for (Fractal *f:fractals)
{
delete f;
f = nullptr;
}
}
I need to read config file, containing key value pairs.
File contains several strings like this:
key1=value1
key2=value2
keyN=valueN
***//terminate
class CFG
{
public:
static void Init(char* path_);
static string Param(string name_);
static string PrintAll();
private:
static void GetPathEXE(string path_);
static void ParseConfigFile();
static map<string, string> mapData;
};
void CFG::ParseConfigFile()
{
ifstream file;
file.open(Param("HomePath") + Param("Slash") + "ConfigMS.cfg");
if (file.is_open())
{
string file_line;
cmatch results;
regex reg3("(.*)=(.*)", std::regex_constants::ECMAScript);
std::cout<<"Parsing cfg file"<<endl;
while (getline(file, file_line) && (file_line.substr(0, 3) != "***"))
{
std::cout<<file_line<<endl;
std::regex_match(file_line.c_str(), results, reg3);
mapData.insert(std::pair<string,string>(results.str(1),results.str(2) ));
mapData[string(results.str(1))] =string( results.str(2));
std::cout<<"key: "<<results.str(1)<<" val: "<<results.str(2)<<endl;
}
//mapData["OuterIP"] = "10.77.1.68";
std::cout<<"Config loaded\n";
for (auto it : mapData)
{
std::cout<<it.first<<"="<<it.second<<endl;
}
if (Param("OuterIP") == "") mapData["Error"] = "IP for receiving messages not set in *.cfg file.\n";
//else if (data["sipName"] == "") error = "sipName for receiving messages not set in *.cfg file.\n";
}
else { mapData["Error"] = "Could not open *.cfg file. Check its existance or name. Name Must \"be ConfigMS.cfg\".\n"; }
if (Param("RTPPort") == Param("MGCPPort"))
{
mapData["Error"] = "RTP port is same as MGCP port. Please change one of them.\n";
}
if (Param("Error") != "")
{
cout << "\n" + Param("Error");
//system("pause");
exit(-1);
}
}
string CFG::Param(string name_)
{
return mapData[name_];
}
If I rely on file input i have [OuterIP,10.77.1.68] key value pair in my map, but function CFG::Param("OuterIP") returns empty string; And I have no idea how to work around this problem;
I've compiled you code with small modifications and it works.
rafix07 told us the program exits if there is no MGCPort and RTPPort values, and he is right, but you told explicitly that the function returns an empty string. Are you sure there isn't another empty OuterIP parameter in the file?
BTW my modifications are removing the path, reading just the file and commenting out the exit(-1) thing. (As well as adding a main function.)
#include <regex>
#include <map>
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
class CFG
{
public:
static void Init(char* path_);
static string Param(string name_);
static void ParseConfigFile();
private:
static void GetPathEXE(string path_);
static map<string, string> mapData;
};
void CFG::ParseConfigFile()
{
ifstream file;
file.open("ConfigMS.cfg");
if (file.is_open())
{
string file_line;
cmatch results;
regex reg3("(.*)=(.*)", std::regex_constants::ECMAScript);
std::cout<<"Parsing cfg file"<<endl;
while (getline(file, file_line) && (file_line.substr(0, 3) != "***"))
{
std::cout<<file_line<<endl;
std::regex_match(file_line.c_str(), results, reg3);
mapData.insert(std::pair<string,string>(results.str(1),results.str(2) ));
mapData[string(results.str(1))] =string( results.str(2));
std::cout<<"key: "<<results.str(1)<<" val: "<<results.str(2)<<endl;
}
//mapData["OuterIP"] = "10.77.1.68";
std::cout<<"Config loaded\n";
for (auto it : mapData)
{
std::cout<<it.first<<"="<<it.second<<endl;
}
if (Param("OuterIP") == "") mapData["Error"] = "IP for receiving messages not set in *.cfg file.\n";
//else if (data["sipName"] == "") error = "sipName for receiving messages not set in *.cfg file.\n";
}
else { mapData["Error"] = "Could not open *.cfg file. Check its existance or name. Name Must \"be ConfigMS.cfg\".\n"; }
if (Param("RTPPort") == Param("MGCPPort"))
{
mapData["Error"] = "RTP port is same as MGCP port. Please change one of them.\n";
}
if (Param("Error") != "")
{
cout << "\n" + Param("Error");
//system("pause");
//exit(-1);
}
}
string CFG::Param(string name_)
{
return mapData[name_];
}
map<string, string> CFG::mapData;
int main ()
{
CFG::ParseConfigFile ();
cout << "Param: " << CFG::Param("OuterIP") << std::endl;
return 0;
}
This is the program as I've compiled with:
g++ main.cc -o regex -std=c++11
This is the configuration file:
OuterIP=10.0.0.1
And these are the results:
Parsing cfg file
key: val:
key: val:
OuterIP=10.0.0.1
key: OuterIP val: 10.0.0.1
Config loaded
=
OuterIP=10.0.0.1
RTP port is same as MGCP port. Please change one of them.
Param: 10.0.0.1
As you can see, the last line is the cout in main.
So your code works. You have to remove the exit(-1) from the class. It doesn't have sense anyway.
I've created a class which is supposed to read in DNA sequences: It contains an if stream private member:
Interface:
class Sequence_stream {
const char* FileName;
std::ifstream FileStream;
std::string FileFormat;
public:
Sequence_stream(const char* Filename, std::string Format);
NucleotideSequence get();
};
Implementation:
Sequence_stream::Sequence_stream(const char* Filename, std::string Format)
{
FileName = Filename;
FileStream.open(FileName);
FileFormat = Format;
std::cout << "Filestream is open: " << FileStream.is_open() << std::endl;
}
NucleotideSequence Sequence_stream::get()
{
if (FileStream.is_open())
{
char currentchar;
int basepos = 0;
std::string name;
std::vector<Nucleotide> sequence;
currentchar = FileStream.get();
if (currentchar == '>' && false == FileStream.eof()) { // Check that the start of the first line is the fasta head character.
currentchar = FileStream.get(); // Proceed to get the full name of the sequence. Get characters until the newline character.
while(currentchar != '\n' && false == FileStream.eof())
{
if (true == FileStream.eof()) {
std::cout << "The file ends before we have even finished reading in the name. Returning an empty NucleotideSequence" << std::endl;
return NucleotideSequence();
}
name.append(1, currentchar);
currentchar = FileStream.get();
} // done getting names, now let's get the sequence.
currentchar = FileStream.get();
while(currentchar != '>' && false == FileStream.eof())
{
if(currentchar != '\n'){
basepos++;
sequence.push_back(Nucleotide(currentchar, basepos));
}
currentchar = FileStream.get();
}
if(currentchar == '>')
{
FileStream.unget();
}
return NucleotideSequence(name, sequence);
} else {
std::cout << "The first line of the file was not a fasta format description line beginning with '>'. Are you sure the file is of FASTA format?" << std::endl;
return NucleotideSequence();
}
} else {
std::cout << "The filestream is not open..." << std::endl;
return NucleotideSequence();
}
}
However if I test it:
int main()
{
std::cout << "Let's try and read in a sequence!" << std::endl;
std::cout << "First we'll create a stream!" << std::endl;
Sequence_stream MyDNAStream("~/Dropbox/1_20dd5.fasta", "fasta");
std::cout << "Done!" << std::endl;
std::cout << "Now let's try and get a sequence!" << endl;
NucleotideSequence firstsequence = MyDNAStream.get();
return 0;
}
I see that the if stream is not open:
Let's try and read in a sequence!
First we'll create a stream!
Filestream is open: 0
Done!
The filestream is not open...
logout
[Process completed]
Although I thought the constructor function opens the if stream. What do I need to do to correct this so as the object is created and contains an open stream? (I know I'm yet to include a destructor which will close the stream upon destruction of the object).
Thanks,
Ben.
Your example shows that is_open returned false. I think you should check in your constructor that the file is indeed open, and throw if not.
In your case, I suspect this is due to passing "~/Dropbox/1_20dd5.fasta" as an input parameter. Did you test with a full pathname, with no ~? I have no knowledge of a C++ library that handles real path expansion (like python's os.path).
I want to read data from a txt file, but i am not able to get it. I am new to c++.
Here is my code, but it does not work. I used getline(),
ifstream inFile;
string sPassWord;
inFile.open("QdatPassWordconfig.config");
inFile.seekg(0,ios::end);
int length=inFile.tellg();
if (inFile.is_open())
{
while (!inFile.eof())
{
getline(inFile,sPassWord);
cout<<sPassWord<<endl;
}
cout<<"get data from txt file"<<endl;
// here ,I cannot read data from file
cout<<sPassWord<<endl;
}
if(!inFile.is_open() || length==0)
{
cout<<"file is create or write"<<endl;
sPassWord="BdsWUjT26";
ofstream outFile;
outFile.open("QdatPassWordconfig.config");
outFile<<sPassWord<<endl;
outFile.close();
}
inFile.close();
cout<<sPassWord<<endl;
It isn't clear if you are trying to read the first line of the file, the last line of the file, or all the lines of the file. Here program snippets for each possibility:
To read the first line of the file:
// UNTESTED
{
ifstream inFile("QdatPassWordconfig.config");
string sPassWord;
if(std::getline(inFile, sPassWord)) {
std::cout << "Password is: " << sPassWord << "\n";
} else {
std::cout << "No password available.\n"
}
}
To read all of the lines of the file:
// TESTED
#include <iostream>
#include <fstream>
#include <string>
int main ()
{
std::ifstream inFile("QdatPassWordconfig.config");
std::string sPassWord;
while(std::getline(inFile, sPassWord)) {
std::cout << "Password is: " << sPassWord << "\n";
}
}
To read the last line of the file:
// UNTESTED
{
ifstream inFile("QdatPassWordconfig.config");
string sPassWord;
int lineCount = 0;
while(std::getline(inFile, sPassWord)) {
lineCount++;
}
if(lineCount) {
std::cout << "Password is: " << sPassWord << "\n";
} else {
std::cout << "No password available.\n";
}
}
inFile.seekg(0,ios::end);
int length=inFile.tellg();
1.You forgot seek back to the beginning. Like this:
inFile.seekg(0,ios::end);
int length=inFile.tellg();
inFile.seekg(0,ios::beg);
2.You need to practice on your if and else statement.
3.Don't use std::ifstream::eof. Use std::getline.
Do something like this:
// Declare local variables
std::ifstream inFile;
std::string sPassword = "";
::UINT length = 0;
// Attempt to open file
inFile.open( "QdatPassWordconfig.config" );
// Use your if and else statement like this:
// Is inFile open?
if( inFile.is_open( ) )
{
// Read file line by line using std::getline
while( std::getline( inFile, sPassword ) ) {
// Print sPassword
std::cout << sPassword << std::endl;
}
// Done with inFile, close it
inFile.close( );
}
else
{
// Do whatever if inFile can't be open
}
There are so many errors with your code, so I decided to show you how I would have done it (please do read the comments):
void Example( void )
{
// DECLARATION
bool bInputMode = true;
std::fstream ioFile;
::UINT nFileSize = 0;
std::string strPassword = "";
// INITIALIZATION
// *Open or create ioFile
// ioFile can now do both input and output operations
ioFile.open( "Passwords.pw",
std::fstream::in |std::fstream::out | std::fstream::app );
// *Calculate/set the value of bInputMode
// first, calculate the size of the file
// if the size of the file is = 0,
// bInputMode = false - which means to be in output mode
ioFile.seekg( 0, std::ios::end );
if( ( nFileSize = ioFile.tellg( ) ) = 0 )
bInputMode = false;
ioFile.seekg( 0, std::ios::beg );
// DO WHATEVER
// *Since bInputMode == true,
// we shall read each line from ioFile by using std::getline
if( bInputMode )
{
// *Get each line within ioFile and "copy" it to strPassword
// and then print strPassword
// *With std::getline, we could get the spaces
while( std::getline( ioFile, strPassword ) )
std::cout << strPassword << std::endl;
}
// *Since bInputMode == false,
// we shall create a new from ioFile and then write to it
else
{
std::cout << "Creating/writing a new file..." << std::endl;
strPassword = "Password123";
ioFile << strPassword << std::endl;
}
// CLEAN-UP
// We are done with ioFile, close it.
ioFile.close( );
};
Please point out any errors! Some feedback and suggestions would be great as well.