Im working on my homework assignment and I stuck because in the assignment we have to ask the user to enter a file name but also to type in either wc cc or lc (word count, character count, and line count of a file. For example, wc filename.txt. Im suppose to check the file to see if its valid or not which i understand and I know how to compare the users input to determine the different kind of function to run, but I dont understand how you could do it together. Any ideas? This is what I have so far.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
string line;
string file;
ifstream input; //input file stream
int i;
cout << "Enter a file name" << endl;
while(true){
cout << ">" ;
getline(cin,file);
input.open(file.c_str());
if (input.fail()) {
cerr << "ERROR: Failed to open file " << file << endl;
input.clear();
}
else {
i = 0;
while (getline(input, line))
if(line == "wc"){
cout << "The word count is: " << endl;
}
else if(line == "cc"){
cout << "The character count is: " << endl;
}
else if(line == "lc"){
cout << "The line count is: " << endl;
}
else if(line == "exit"){
return 0;
}
else{
cout << "----NOTE----" << endl;
cout << "Available Commands: " << endl;
cout <<"lc \"filename\"" << endl;
cout <<"cc \"filename\"" << endl;
cout <<"wc \"filename\"" << endl;
cout <<"exit" << endl;
}
}
}
return 0;
}
void wordCount(){
//TBD
}
void characterCount(){
//TBD
}
void lineCount(){
//TBD
}
You have to find the space between the command and the file name in the users input and then split the string where you find the space. Something like this
cout << "Enter a command\n";
string line;
getline(cin, line);
// get the position of the space as an index
size_t space_pos = line.find(' ');
if (space_pos == string::npos)
{
// user didn't enter a space, so error message and exit
cout << "illegal command\n";
exit(1);
}
// split the string at the first space
string cmd = line.substr(0, space_pos);
string file_name = line.substr(space_pos + 1);
This is untested code.
You could do better than this, for instance this would not work if the user entered two spaces between the command and the file name. But this kind of work rapidly gets very tedious. As this is an assignment I would be tempted to move on to more interesting things. You can always come back and improve things later if you have the time.
I think you are asking how to validate multiple arguments: the command and the file.
A simple strategy is to have function like the following:
#include <fstream> // Note: this is for ifstream below
bool argumentsInvalid(const string& command, const string & command) {
// Validate the command
// Note: Not ideal, just being short for demo
if("wc" != command && "cc" != command && "lc" != command) {
std::cout << "Invalid command" << std::endl;
return false;
}
// Validate the file
// Note: This is a cheat that uses the fact that if its valid, its open.
std::ifstream fileToRead(filename);
if(!fileToRead) {
std::cout << "Invalid file: \"" << filename << "\"" << std::endl;
return false;
}
return true;
// Note: This does rely on the ifstream destructor closing the file and would mean
// opening the file twice. Simple to show here, but not ideal real code.
}
If you want to evaluate ALL arguments before returning an error, insert a flag at the top of that function, like:
// To be set true if there is an error
bool errorFound = false;
and change all of the returns in the conditions to:
errorFound = true;
and the final return to:
return !errorFound;
Usage:
....
if(argumentsInvalid(command, filename)) {
std::cout << "Could not perform command. Skipping..." << std::endl;
// exit or continue or whatever
}
// Now do your work
Note: The specific validity tests here are over simplified.
Related
I'm trying to write a program that replaces a specific line with a word in it or if the line doesn't exist in the file it should add it. Kind of like a key value store.
I played around with the following code, unfortunately nothing came of it. Either he just overwrote the file and deleted the other lines, or he just didn't write anything at all. Finally, this code remained. It's inefficient and confusing, I know that but maybe you can help me.
!!! at the moment he is not writing anything to the file!!!
Code:
void edit(char*key, char*value){
ifstream filein("test.txt");
ofstream fileout("tempTest.txt");
if(!filein || !fileout)
{
cout << "Error opening files!" << endl;
return;
}
bool found = false;
string line;
while(getline(filein, line))
{
if(line.find(key) == 0)
{
fileout << key << "=" << value << endl;
found = true;
}
else
{
fileout << line << endl;
}
if(!found)
{
fileout << key << "=" << value << endl;
}
}
filein.close();
fileout.close();
remove("test.txt");
}
In your while loop, you keep writing
fileout << key << "=" << value << endl;
in every iteration, until your key is found.
That line should be outside of the loop.
I wrote this piece of code to try leveldb. I am using Unix time as keys. For values that have spaces, only the last part gets saved. Here is the code. I am running Linux Kernel 4.4.0-47-generic
while (true) {
std::string note;
std::string key;
std::cout << "Test text here ";
std::cin >> note;
std::cout << std::endl;
if(note.size() == 0 || tolower(note.back()) == 'n' ) break;
key = std::to_string(std::time(nullptr));
status = db->Put(write_options, key, note);
if(!status.ok()) break;
}
std::cout << "Read texts........" << std::endl;
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for(it->SeekToFirst(); it->Valid(); it->Next()){
std::cout << it->key().ToString() << " " << it->value().ToString() << std::endl;
}
delete db;
The issue is not in leveldb, but in the way you read the input:
std::string note;
std::cin >> note;
This will read only up to the first whitespace. It is common mistake, see for example:
reading a line from ifstream into a string variable
so I worked on my program and now I am on a point where I can not find a solution. I need to replace some more signs in the fext file, for now the program only replaces "TIT" with the code number "*245$a", if I want to replace other letters the same way, the program does not change. Does anybody know how I can implement some more replacements in the text file? Let me know if there is a better option to replace more than 5 signs with another ones.
Thank you
#include <fstream>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main()
{
char dateiname[64], kommando[64];
ifstream iStream;
cout << "Choose an activity" << endl <<
" s - search " << endl <<
" c - convert" << endl <<
" * - end program" << endl;
cin.getline(kommando,64,'\n');
switch(kommando[0])
{
case 'c':
cout << "Enter a text file!" << endl;
cin.getline(dateiname,64,'\n');
iStream.open("C://users//silita//desktop//schwarz.txt");
case 's':
break;
case '*':
return 0;
default:
cout << "I can not read " << kommando << endl;
}
if (!iStream)
{
cout << "The File" << dateiname << "does not exist." << endl;
}
string s;
char o[] = "TIT";
while (getline(iStream, s))
{
while(s.find(o, 0) < s.length())
s.replace(s.find(o, 0), s.length() - s.find(o, 3),"*245$a");
cout << s << endl;
}
iStream.close();
}
You can use map in C++ STL to store multiple convert rules:
#include<map>
#include<algorithm>
using namespace std;
map<string,string> convertRules;
typedef map<string,string>::iterator MIT;
void setConvertRules(int numOfRules){
string word,code;
for(int i = 0 ; i < numOfRules; ++i){
cin>>word>>code;
//Use code as search key in order to decrypt
//If you want to encrypt, use convertrules[word] = code;
convertRules[code] = word;
}
}
To convert a file, just do as follows (some functions and classes need to be declared and implemented first, but here we mainly focus on top-level design):
/* Detailed class implementations are omitted for simplicity */
//a class to store contents of a file
class File;
//a processor to read, insert and overwrite certain file
class FileProcessor;
void FileProcessor::convert(const string &code, const string &word){
cursor == file.begin();
while(cursor != fp.end()){
_fp.convertNextLine(code,word);
}
}
File file;
FileProcessor filePcr;
int main()
const string sourceDir = "C://users//silita//desktop//schwarz.txt";
const string destDir = "C://users//silita//desktop//schwarz_decrypted.txt";
//Open a .txt file and read in its contents
if (!file.openAndReadIn(sourceDir)){
cerr << "The File" << sourceDir << "does not exist." << endl;
abort();
}
//Try to link processor to open file
if(!fp.linkTo(file)){
cerr << "Access to file" << sourceDir << "is denied." << endl;
abort();
}
//iterator is like a more safe version of C-style pointer
//the object type is a string-string pair
for(MIT it = convertRules.begin(); it != convertRules.end(); ++it){
fp.convert(it->first, it->second);
}
file.saveAs(destDir);
return 0;
}
Finally, if I would suggest you use C-style strstr function for efficiency when dealing with large files or batch processing. string::find adopts a naive sequential search startegy while strstr is implemented with the famous KMP algorithm for fast pattern match in strings, which is both efficient and thorough(can replace all matchs in one go instead of another for-loop).
I am looking to find a C++ fstream equivalent function of C fgets. I tried with get function of fstream but did not get what I wanted. The get function does not extract the delim character whereas the fgets function used to extract it. So, I wrote a code to insert this delim character from my code itself. But it is giving strange behaviour. Please see my sample code below;
#include <stdio.h>
#include <fstream>
#include <iostream>
int main(int argc, char **argv)
{
char str[256];
int len = 10;
std::cout << "Using C fgets function" << std::endl;
FILE * file = fopen("C:\\cpp\\write.txt", "r");
if(file == NULL){
std::cout << " Error opening file" << std::endl;
}
int count = 0;
while(!feof(file)){
char *result = fgets(str, len, file);
std::cout << result << std::endl ;
count++;
}
std::cout << "\nCount = " << count << std::endl;
fclose(file);
std::fstream fp("C:\\cpp\\write.txt", std::ios_base::in);
int iter_count = 0;
while(!fp.eof() && iter_count < 10){
fp.get(str, len,'\n');
int count = fp.gcount();
std::cout << "\nCurrent Count = " << count << std::endl;
if(count == 0){
//only new line character encountered
//adding newline character
str[1] = '\0';
str[0] = '\n';
fp.ignore(1, '\n');
//std::cout << fp.get(); //ignore new line character from stream
}
else if(count != (len -1) ){
//adding newline character
str[count + 1] = '\0';
str[count ] = '\n';
//std::cout << fp.get(); //ignore new line character from stream
fp.ignore(1, '\n');
//std::cout << "Adding new line \n";
}
std::cout << str << std::endl;
std::cout << " Stream State : Good: " << fp.good() << " Fail: " << fp.fail() << std::endl;
iter_count++;
}
std::cout << "\nCount = " << iter_count << std::endl;
fp.close();
return 0;
}
The txt file that I am using is write.txt with following content:
This is a new lines.
Now writing second
line
DONE
If you observe my program, I am using fgets function first and then using the get function on same file. In case of get function, the stream state goes bad.
Can anyone please point me out what is going wrong here?
UPDATED: I am now posting a simplest code which does not work at my end. If I dont care about the delim character for now and just read the entire file 10 characters at a time using getline:
void read_file_getline_no_insert(){
char str[256];
int len =10;
std::cout << "\nREAD_GETLINE_NO_INSERT FUNCITON\n" << std::endl;
std::fstream fp("C:\\cpp\\write.txt", std::ios_base::in);
int iter_count = 0;
while(!fp.eof() && iter_count < 10){
fp.getline(str, len,'\n');
int count = fp.gcount();
std::cout << "\nCurrent Count = " << count << std::endl;
std::cout << str << std::endl;
std::cout << " Stream State : Good: " << fp.good() << " Fail: " << fp.fail() << std::endl;
iter_count++;
}
std::cout << "\nCount = " << iter_count << std::endl;
fp.close();
}
int main(int argc, char **argv)
{
read_file_getline_no_insert();
return 0;
}
If wee see the output of above code:
READ_GETLINE_NO_INSERT FUNCITON
Current Count = 9
This is a
Stream State : Good: 0 Fail: 1
Current Count = 0
Stream State : Good: 0 Fail: 1
You would see that the state of stream goes Bad and the fail bit is set. I am unable to understand this behavior.
Rgds
Sapan
std::getline() will read a string from a stream, until it encounters a delimiter (newline by default).
Unlike fgets(), std::getline() discards the delimiter. But, also unlike fgets(), it will read the whole line (available memory permitting) since it works with a std::string rather than a char *. That makes it somewhat easier to use in practice.
All types derived from std::istream (which is the base class for all input streams) also have a member function called getline() which works a little more like fgets() - accepting a char * and a buffer size. It still discards the delimiter though.
The C++-specific options are overloaded functions (i.e. available in more than one version) so you need to read documentation to decide which one is appropriate to your needs.
I'm having a weird issue. I'm writing a function to delete a line from a list of names created elsewhere, which, after some research, seems like it should be fairly simple. I write the current list of names into a list, display the list, have the user input the name they want to delete, remove the user-inputted name from the list, then display the updated list to the user.
Up to here, everything works perfectly, but when I write the list back into the file, the last name gets a random amount of characters chopped off of it, ranging from a couple of characters to the entire line. Now, this is where it gets strange. If I open the file and look at it without exiting the program, the last line of the file is messed up and continues to be whenever I display it later in the program. But, if I exit the program and then open the file, the last line is back to how it was originally written! That file is not written to again by the program after the list is written in, so I cannot imagine why this is happening.
I almost decided that since the file ultimately comes out of the program correct, I could just ignore the issue, but I want the user to be able to view the list of names after the deletion for various reasons, which is made impossible while the last list item prints incorrectly.
I am still fairly beginner with C++, so I'm kind of hoping that this is just an issue of me not fully understanding lists or something. Regardless, dumbed down explanations would be ace.
I included the function below, any help is much appreciated.
char act, charname[50];
string namestr;
list <string> c1;
list <string>::iterator c1_Iter;
//write the names from the file into a list
ifstream names("List of Names.txt");
while (std::getline(names, namestr))
{
c1.push_back(namestr);
}
//print the current names
cout << "Registered names:";
for (c1_Iter = c1.begin(); c1_Iter != c1.end(); c1_Iter++)
cout << "\n" << setw(5) << " " << *c1_Iter;
//choose which names to delete and confirm
cout << "\n\nEnter the name you would like to delete: ";
cin.getline(charname, 50);
cin.getline(charname, 50);
cout << "\nAre you sure? Enter 'y' to permanently delete " << charname << ", and any other key to return to the start screen.";
cin >> act;
if (act == 'y' || act == 'Y')
{
//delete a file associated with each name
string strname(charname);
strname.append(".txt");
if (remove(strname.c_str()) < 0)
perror("Error deleting file");
else
{
//delete name from the file only if that person's individual file is successfully deleted
c1.remove(charname);
cout << "\n" << charname << " successfully deleted!\n";
//print the updated list of names
cout << "\nUpdated list of registered names:\n";
for (c1_Iter = c1.begin(); c1_Iter != c1.end(); c1_Iter++)
cout << *c1_Iter << endl;
//write updated list of names over "List of Names" to update the file
ofstream newNames("List of Names.txt");
for (c1_Iter = c1.begin(); c1_Iter != c1.end(); c1_Iter++)
newNames << *c1_Iter << endl;
newNames.close();
}
}
As Mohit Jain mentioned in the comments, you need to call names.close() on the ifstream before opening the file for writing as a separate ofstream. Also, you can use a std::string charname rather than char charname[50].
You could also use an fstream with appropriate seeking. If I'm not mistaken have active ifstream and ofstream objects handling the same file can lead to undefined behavior.
Here's a more C++ friendly solution:
#include <iostream>
#include <string>
#include <fstream>
#include <list>
#include <iomanip>
int main()
{
char act;
std::string charname;
std::string namestr;
std::list<std::string> c1;
std::list<std::string>::iterator c1_Iter;
//write the names from the file into a list
std::ifstream names("names.txt");
while (std::getline(names, namestr))
{
c1.push_back(namestr);
}
//print the current names
std::cout << "Registered names:";
for (c1_Iter = c1.begin(); c1_Iter != c1.end(); c1_Iter++)
std::cout << "\n" << std::setw(5) << " " << *c1_Iter;
//choose which names to delete and confirm
std::cout << "\n\nEnter the name you would like to delete: ";
std::cin >> charname;
std::cout << "\nAre you sure? Enter 'y' to permanently delete " << charname << ", and any other key to return to the start screen.";
std::cin >> act;
if (act == 'y' || act == 'Y')
{
//delete a file associated with each name
std::string strname(charname);
strname.append(".txt");
if (remove(strname.c_str()) < 0)
{
std::cerr << "Error deleting file " << strname << std::endl;
return 1;
}
else
{
//delete name from the file only if that person's individual file is successfully deleted
c1.remove(charname);
std::cout << "\n" << charname << " successfully deleted!\n";
//print the updated list of names
std::cout << "\nUpdated list of registered names:\n";
for (c1_Iter = c1.begin(); c1_Iter != c1.end(); c1_Iter++)
std::cout << *c1_Iter << std::endl;
//write updated list of names over "List of Names" to update the file
names.close(); //Close the ifstream before opening the file for editing
std::ofstream newNames("names.txt");
for (c1_Iter = c1.begin(); c1_Iter != c1.end(); c1_Iter++)
newNames << *c1_Iter << std::endl;
newNames.close();
}
}
return 0;
}