String turns up empty after find_last_of() and substr()? - c++

Self-teaching myself C++, and I know I'm missing something critical, but I can't for the life of me figure out what it is.
Forgive the huge block of code, I was tempted to trim it down to the critical elements, but I figured if I left it intact, you folks might have other educational criticisms about my coding style...
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
// main routine
int main(int argc, char *argv[]) {
// will store filetype here for later
string filetype = "";
string filename;
// if no arguments, die.
if (argc < 2) {
cout << "ERROR: Nothing to do." << endl;
return 1;
}
// if more than one argument, die.
else if (argc > 2) {
// TODO: support for multiple files being checked would go here.
cout << "ERROR: Too many arguments." << endl;
return 1;
}
// otherwise, check filetype
else {
string filename = argv[1];
cout << "Filename: " << filename << endl;
//searching from the end, find the extension of the filename
int dot = filename.find_last_of('.');
if (dot == -1){
// TODO: Add support for filenames with no extension
cout << "ERROR: Filename with no extension." << endl;
return 1;
}
string extension = filename.substr(dot);
if (extension == ".htm" || extension == ".html"){
filetype = "html";
}
else if (extension == ".c"){
filetype = "c";
}
else if (extension == ".c++" || extension == ".cpp") {
filetype = "cpp";
}
else {
cout << "ERROR: unsupported file extension" << endl;
// TODO: try to guess filetype from file headers here
}
}
cout << "Determined filetype: " << filetype << endl;
cout << "Filename: " << filename << endl;
return 0;
}
// All done :]
The issue I'm having is mysterious. I put the argument passed into a string like so:
string filename = argv[1];
and then search it for an extension, starting from the end and working my way to the beginning:
int dot = filename.find_last_of('.');
string extension = filename.substr(dot);
This all works as expected, but afterwards, when I try to output filename, it is mysteriously empty? I tried debugging with cout. When I print out the string BEFORE I search it, it prints properly. After, nothing prints. Like so:
$ g++ test.cpp -o test.out; ./test.out foo.html
Filename: foo.html
Determined filetype: html
Filename:
I remembered something about iterators in the past, and tried using filename.begin() to reset it, but this did nothing. Can someone shed light onto this puzzling issue?

You are declaring a second variable called filename here, after the else:
string filename = argv[1];
This goes out of scope by the time you get here:
cout << "Filename: " << filename << endl;
You are now printing the contents of the 1st variable you declared called filename, just under main.

Related

How do I handle empty files when dumping ifstream to cout?

I'm trying to dump the contents of a file to cout.
#include <iostream>
#include <fstream>
int main(int argc, char** argv)
{
if (argc > 1) {
std::ifstream fin(argv[1]);
if (fin) {
std::cout << "---file contents---\n";
std::cout << fin.rdbuf();
std::cout << "---end contents---\n";
} else {
std::cout << "The file does not exist\n";
}
}
else {
std::cout << "Usage: " << argv[0] << " FILE\n";
}
if (std::cout.good()) {
return 0;
}
else if (std::cout.fail()) {
return 1;
}
else {
return 2;
}
}
This code does not work as intended when the input file is empty. It prints the initial "---file contents---", but never prints the trailing "---end contents---". After debugging, I found the application is not crashing, but instead is putting std::cout in an error state (the return code is 1).
How can I print the contents of an empty file without putting cout in an error state?
This operator<< reference (overload number 10 in the list) explains it all:
If no characters were inserted, executes setstate(failbit).
Since the input file is empty, there's no characters to insert into the output stream. And the failbit is set.
You need to add a specific check for failbit after
std::cout << fin.rdbuf();
to see if the input file was empty or not.

ifstream: /dev/stdin is not working the same as std::cin

For my formation, an exercise ask us to create a program similar to the linux 'cat' command.
So to read the file, i use an ifstream, and everything work fine for regular file.
But not when i try to open /dev/ files like /dev/stdin: the 'enter' is not detected and so, getline really exit only when the fd is being closed (with a CTRL-D).
The problem seems to be around how ifstream or getline handle reading, because with the regular 'read' function from libc, this problem is not to be seen.
Here is my code:
#include <iostream>
#include <string>
#include <fstream>
#include <errno.h>
#ifndef PROGRAM_NAME
# define PROGRAM_NAME "cato9tails"
#endif
int g_exitCode = 0;
void
displayErrno(std::string &file)
{
if (errno)
{
g_exitCode = 1;
std::cerr << PROGRAM_NAME << ": " << file << ": " << strerror(errno) << std::endl;
}
}
void
handleStream(std::string file, std::istream &stream)
{
std::string read;
stream.peek(); /* try to read: will set fail bit if it is a folder. */
if (!stream.good())
displayErrno(file);
while (stream.good())
{
std::getline(stream, read);
std::cout << read;
if (stream.eof())
break;
std::cout << std::endl;
}
}
int
main(int argc, char **argv)
{
if (argc == 1)
handleStream("", std::cin);
else
{
for (int index = 1; index < argc; index++)
{
errno = 0;
std::string file = std::string(argv[index]);
std::ifstream stream(file, std::ifstream::in);
if (stream.is_open())
{
handleStream(file, stream);
stream.close();
}
else
displayErrno(file);
}
}
return (g_exitCode);
}
We can only use method from libcpp.
I have search this problem for a long time, and i only find this post where they seems to have a very similar problem to me:
https://github.com/bigartm/bigartm/pull/258#issuecomment-128131871
But found no really usable solution from them.
I tried to do a very ugly solution but... well...:
bool
isUnixStdFile(std::string file)
{
return (file == "/dev/stdin" || file == "/dev/stdout" || file == "/dev/stderr"
|| file == "/dev/fd/0" || file == "/dev/fd/1" || file == "/dev/fd/2");
}
...
if (isUnixStdFile(file))
handleStream(file, std::cin);
else
{
std::ifstream stream(file, std::ifstream::in);
...
As you can see, a lot of files are missing, this can only be called a temporary solution.
Any help would be appreciated!
The following code worked for me to deal with /dev/fd files or when using shell substitute syntax:
std::ifstream stream(file_name);
std::cout << "Opening file '" << file_name << "'" << std::endl;
if (stream.fail() || !stream.good())
{
std::cout << "Error: Failed to open file '" << file_name << "'" << std::endl;
return false;
}
while (!stream.eof() && stream.good() && stream.peek() != EOF)
{
std::getline(stream, buffer);
std::cout << buffer << std::endl;
}
stream.close();
Basically std::getline() fails when content from the special file is not ready yet.

C++ problem trying to open a .txt file using Ifstream

This small bit of code is designed to look through a text file and identify account numbers that have already been written so that later on in my program, you can find the correct account without the error of two accounts with the same account number(id). But no matter what i do, whether its using double backslashes, forward slashes, or double forward slashes in the location for the ifstream object; i always get "cannot find file" as the output.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream accountsread("‪G:/Coding/Test/test/test/accounts.txt");
if (accountsread.is_open()) {
int tempAccno;
std::string tempname;
char tempchar;
int accountsfound = 0;
int input;
std::cout << "Enter the ID of the account \n";
cin >> x;
while (!accountsread.eof()) {
accountsread >> tempAccno;
if (tempAccno == input) {
accountsfound++;
}
else {}
}
if (accountsfound > 0) {
cout << "number found";
}
else {
cout << "number not found";
}
}
else {
cout << "cannot find file";
}
}
in windows, the location of the text file is ‪G:\Coding\Test\test\test\accounts.txt
std::ifstream can use relative paths as well as absolute ones. For your problem, I'd recommend looking into the <filesystem> header from the STL if you really need an absolute path to your file. However, if it's in the same directory as your working directory, you don't need to use absolute paths. Here's how I'd accomplish your task
#include <iostream>
#include <fstream>
#include <string> // Should include since you're using std::string
// Note that I am NOT "using namespace std;"
int main()
{
std::ifstream accountsRead("accounts.txt");
if (accountsRead.is_open())
{
int account_id;
bool account_found = false;
std::cout << "Enter the ID of the account: ";
while (!(std::cin >> account_id))
{ // This loop handles if the user inputs non-number
std::cout << "Please enter a NUMBER below!\n";
std::cout << "Enter: ";
std::cin.ignore(10000, '\n');
std::cin.clear();
}
int tmpAccNum;
while (accountsRead >> tmpAccNum)
{ // This loop reads the file, line by line, into tmpAccNum
if (tmpAccNum == account_id)
{
account_found = true;
break;
}
}
if (account_found)
{
std::cout << "Number found!" << std::endl;
}
else
{
std::cout << "Number not found..." << std::endl;
}
}
else
{ // Error opening file
std::cout << "File not found or is corrupted" << std::endl;
}
}
A few things about your code stylistically speaking. First, you should never be using namespace std, and (if you are for some reason) there isn't a reason to mix and match specifying the std namespace on only some std members. Second, you don't need to specify an else for every if-statement, and you probably shouldn't unless there actually are commands to execute if the else case is reached.
Edit
If you still need an absolute path, here is how you can do that:
#include <filesystem>
int main()
{
// Create path object for the file path
std::filesystem::path file_path("G:\Coding\Test\test\test\accounts.txt");
// The '.string()' method for a 'std::path' object returns the string
// version of the path, so you can use it with an 'std::ifstream'
std::ifstream accounts(file_path.string()); // Opens file via 'ifstream'
/* And so on... */
}

Multiple definition error on the same line. (C++)

I have a new complicated problem. The compiler complains that I am redefining a function, but it says that the first place I declared it at has the site of re-declaration. The problem began as soon as I included the cpp file in another. In attempt to fix my problem I exported it to a hpp file, but to know avail. Here is my code.
main.cpp:
#include <iostream>
#include <string>
#include "main.hpp"
using namespace std;
int main(int argc, char *argv[])
{
//Deal with arguments and send them to the correct functions
if (argc >= 2){
string op = argv[1];
if (op == "-a" || op == "--automatic"){
if (argc >= 3){
string FName = argv[2];
bool dbgbool;
if (argc == 4){
string dbgstring = argv[3];
if (dbgstring == "debug"){
dbgbool = true;
}
}
Lexer(FName, dbgbool);
}
}
else{
cout << "Invalid Argument\n";
goto help;
}
return 0;
}
//Or, just write help and info
help:
cout << "\n";
cout << "bwc v0.0.1U-(Unstable)\n\n";
cout << "Usage: bwc <operation> [...]\n";
cout << "Operations:\n";
cout << " bwc {-a --automatic} <file(s)>\n";
cout << " bwc {-i --interactive}\n";
cout << " bwc {-c --error-codes}\n";
cout << "\n";
return 0;
}
LA.cpp:
#include<iostream>
#include<fstream>
#include<string>
#include<sstream>
using namespace std;
string Lexer(string FileN, bool dbg){ //This is the line of re-declaration.
//If debugging,this writes out put to the console
if (dbg == true)
cout << "Beginning Lexical Analysis...\n";
//Create new file stream and set it equal to the source file
ifstream Ifile (FileN.c_str());
//Test if the last step failed, if so, write an error to the console, and terminate the compiler
if (!Ifile.is_open()){
cout << "Unable to open file. Path to file may not exist, or the file name could be incorrect.\n";
cout << "Error Code: -1\n";
return NULL;}
//Create new stringstream, and set it equal to the source file
string IFStream;
Ifile >> IFStream;
//Close the source file
Ifile.close();
//If debugging,this writes out put to the console
if (dbg == true)
cout << "Source file sucessfully read.\n";
//Set out stream equal to the modified in stream
string OFStream = IFStream;
return OFStream;
}
and finally,
main.hpp:
#ifndef MAIN_HPP_INCLUDED
#define MAIN_HPP_INCLUDED
#include "LA.cpp"
extern string Lexer(string,bool);
#endif // MAIN_HPP_INCLUDED
Thanks,
Brooks Rady
Your main.cpp is including main.hpp which is including LA.cpp, so the contents of LA.cpp are being compile once for LA.cpp and once for main.cpp.
.hpp files should contain only declarations (string Lexer(string,bool);), while the definitions (string Lexer(string,bool) {... }) should go in the .cpp
You will not see this kind of issue when you are dealing with class methods, because the compiler accepts definitions of methods. But functions should be defined only in the .cpp files.

Variable length and position command line arguments

So, quick question:
I am tasked with making a program that reads in a file, does some fancy things, and writes out to an html file with the re-purposed text, all simple stuff.
Furthermore the program must be able to accept up to four command line arguments (but a minumum of two). The executable of course, the file it is reading in, the file name it will be reading out to, and finally a "-r" argument for more information on the file (the amount of paragraphs, etc).
So the question I have is as follows:
the "-r" argument can be anywhere in the arguments (as long as it comes after argv[0] of course), or it can be completely non-existent (as can the output file name).
This is still simple to do, a little tedious writing a bunch of if's or even a switch or two, but I can't help but think that there may be an easier way to accomplish this, rather than having a plethora of if statements.
Any help would be greatly appreciated. (I also suppose you don't need any code considering I don't exactly have a problem.)
Here is roughly how one might do it without a library:
GCC 4.8.2: g++ -Wall -Wextra -std=c++0x main.cpp
#include <iostream>
#include <string>
int main(int argc, char* argv[]) {
char* input = nullptr;
char* output = nullptr;
char* r_arg = nullptr;
--argc; ++argv; // Skip the program name.
const char* def_in = "default in";
const char* def_out = "default out";
const char* def_r = "default r";
while (0 < argc) {
if (std::string(argv[0]) == "-r") {
// This code requires a space after the "-r", which is unusual.
++argv;
--argc;
r_arg = argv[0]; }
else if (input == nullptr) {
input = argv[0]; }
else if (output == nullptr) {
output = argv[0]; }
else {
std::cerr << "error: unexpected arg '" << argv[0] << "'\n"; }
++argv;
--argc; }
if (input == nullptr) {
input = const_cast<char*>(def_in); }
if (output == nullptr) {
output = const_cast<char*>(def_out); }
if (r_arg == nullptr) {
r_arg = const_cast<char*>(def_r); }
std::cout << "input: " << input << "\n"
<< "output: " << output << "\n"
<< "r arg: " << r_arg << "\n";
return 0; }