bool remove_vowels(const std::string& file_name) {
std::fstream fs{ file_name , std::ios_base::in | std::ios_base::out };
if (!fs) { std::cout << "Error file not open"; return false; }
char in{};
while (fs >> in) {
switch (tolower(in)) {
case 'a': case 'e': case 'i': case 'o': case 'u':
{
int64_t pos{ fs.tellg() };
fs.seekp(pos - 1);
fs << ' ';
fs.seekg(pos);
break;
}
}
}
return true;
}
I try to solve one exercise from: Programming: Principles and Practice Using C++ Bjarne Stroustrup
Write a program that removes all vowels from a file (“disemvowels”). For
example, Once upon a time! becomes nc pn tm!. Surprisingly often, the
result is still readable; try it on your friends.
The text file contain: "Once upon a time"
When i try this code inside the switch case:
int64_t pos { fs.tellg() }; // it get position one character forward
fs.seekp(pos - 1); // i set the write position to the the character
fs << ' '; // then i replace the vowel to whitespace and increment one position
i get infinite loop that overwrite all the file with these characters "nc "
its only work for me this way:
int64_t pos{ fs.tellg() };
fs.seekp(pos - 1);
fs << ' ';
fs.seekg(pos);
Why it is necessary to set the position after fs << ' '; if its already increases it?
Related
I want to hold a string with spaces therefore I used getline() but after it I want to get another string(no spaces) if there is a -e for example and the string after it in s2, but since in my code I lose the dash when using getline() I can't seem to achieve what I'm trying to do. any suggestions would be really helpful.
//example input: -f name -b blah blah -e email
//looking for output:
//name
//blah blah
//email
string s,s1,s2;
char check_character;
while (cin.peek() != '\n')
{
if (cin.get() == '-')
{
check_character = cin.get();
switch(check_character)
{
case 'f':
cin >> s;
break;
case 'b':
if(cin.peek() != '\n')
getline(cin, s1, '-');
else if(cin.peek() =='\n')
getline(cin, s1);
break;
case 'e':
cin>> s2;
break;
}
}
}
cout << s << endl << s1 << endl << s2 << endl;
return 0;
}
A better option would be to do a single call to getline() then parse the "command" string. There are many options of achieving this, from a simple split() on "-" or find('-')
getline() extracts characters from is and stores them into str until the delimitation character delim is found or the newline character, '\n'.
If the delimiter is found, it is extracted and discarded (i.e. it is not stored and the next input operation will begin after it).
I'm going to make a couple assumptions here:
You never expect a '\n' except at the end of the input string, even after "-b" (which your code will currently read in)
You expect to only accept 1 of each type of argument (cause your current code will stomp any previous entries)
A regex_search will handle this nicely with the regex:
(?:\s*-f\s+(\w+)|\s*-b\s+([^-]+)|\s*-e\s+(\w+))*
Live Example
You'll need to start by reading from cin into a variable, for example string input. This could be done like:
getline(cin, input)
Once you have your input you can simply do:
if(smatch m; regex_search(input, m, regex{ "(?:\\s*-f\\s+(\\w+)|\\s*-b\\s+([^-]+)|\\s*-e\\s+(\\w+))*" })) {
if(m[1].length() > 0U) {
cout << "-f " << m[1] << endl;
}
if(m[2].length() > 0U) {
cout << "-b " << m[2] << endl;
}
if(m[3].length() > 0U) {
cout << "-e " << m[3] << endl;
}
}
Live Example
If you go the approach of reading in the entire line, and you do not want to use Boost program options, or getopts, you could parse the line yourself (as has been suggested). Here would be one way of doing it, as an alternative of parsing on the fly in your code:
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
using std::cout;
using std::endl;
using std::get;
using std::literals::string_literals::operator""s;
using std::make_tuple;
using std::string;
using std::tuple;
using std::vector;
static auto chunkLine(string const& line)
{
auto result = vector<string>{};
auto i = string::size_type{};
while (i != string::npos && i < line.size())
{
auto pos = line.find(" -", i);
auto count = pos == string::npos ? pos : (pos - i);
result.push_back(line.substr(i, count));
i = pos + (pos != string::npos ? 1 : 0);
}
return result;
}
static auto parseChunks(vector<string> const& chunks)
{
auto result = vector<tuple<string, string>>{};
for (auto const& chunk : chunks)
{
auto pos = chunk.find(" ");
if (pos != string::npos && chunk[0] == '-')
{
auto kv = make_tuple(chunk.substr(1, pos-1), chunk.substr(pos+1));
result.push_back(kv);
}
}
return result;
}
int main()
{
auto line = "-f name -b blah blah -e email"s;
auto keyValueTuples = parseChunks(chunkLine(line));
for (auto const& kv : keyValueTuples)
{
cout << get<1>(kv) << endl;
}
}
The way you parse the arguments could certainly be improved, but this answer is not about it. I think what you are looking for is to simply put the - char back into the stream after std::getline removed it. In this case you could just use .putback() method
if (std::cin.peek() != '\n')
{
std::getline(std::cin, s1, '-');
std::cin.putback('-');
}
I think you should put cin.ignore before typing getline as in your code:
`string s,s1,s2;
char check_character;
while (cin.peek() != '\n')
{
if (cin.get() == '-')
{
check_character = cin.get();
switch(check_character)
{
case 'f':
cin >> s;
break;
case 'b':
if(cin.peek() != '\n')
cin.ignore
getline(cin, s1, '-');
else if(cin.peek() =='\n')
cin.ignore
getline(cin, s1);
break;
case 'e':
cin>> s2;
break;
}
}
}
cout << s << endl << s1 << endl << s2 << endl;
return 0; `
So I'm testing out a small program that reads a file containing a few lines of one or two characters each and, based on what characters are in the line, outputs directional arrows (Think DDR output style: "< ^ v >")
Just reading and outputting the lines as they appear in the text file works fine, but I can't edit the way they appear without getting a super generic debug error with no code or anything to tell me what the problem is. Is this just something I'm not allowed to do, or should I replace my string with an array?
Code below:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void output(string raw);
int main()
{
string content_line;
ifstream file;
file.open("level1.txt", ios::out);
if (file.is_open())
{
while (getline(file, content_line))
{
output(content_line);
}
file.close();
}
return 0;
}
void output(string raw)
{
string row = " ";
for (int i = 0; i <= raw.size(); i++)
{
switch (raw.at(i))
{
case 'l': row.at(0) = '<';
break;
case 'u': row.at(2) = '^';
break;
case 'd': row.at(4) = 'v';
break;
case 'r': row.at(6) = '>';
break;
}
}
cout << row << '\n';
}
The text file as it appears right now is just:
u
d
l
r
ud
lr
ul
dr
So I have searched for quite a while and found nothing that solves my problem currently I have a mess of a code from my previous problem, can someone attempt to tidy this so it works or simply remake it, I have 4 different text files to read from depending on what it is so the switch statement was my best idea and I intend to keep that bit. Just to add this code does not work.. .and can't work out why not.
ifstream QuestionFile;
int i = 0;
switch (x){
case 1:
QuestionFile.open("Topic1 Questions.txt", ios::app);
break;
case 2:
QuestionFile.open("Topic2 Questions.txt", ios::app);
break;
case 3:
QuestionFile.open("Topic3 Questions.txt", ios::app);
break;
case 4:
QuestionFile.open("Topic4 Questions.txt", ios::app);
break;
}
stringstream buffer;
buffer << QuestionFile.rdbuf();
string test = buffer.str();
size_t pos1 = 0;
size_t pos2;
if (!QuestionFile)
{
cout << "Cannot load file" << endl;
}
else
{
if (QuestionFile.peek() != ifstream::traits_type::eof()) {
while (!QuestionFile.eof())
{
pos2 = test.find("|", pos1);
questions[i] = test.substr(pos1, (pos2 - pos1));
cout << questions[i];
i++;
}
QuestionFile.close();
}
}
Okay so solved this myself in the end was actually really simple... so just in case anyone else comes across this and doesn't find a simple answer I've done it like this instead.
ifstream QuestionFile;
int i = 0;
//switch statement for checking which text file I personally wanted to use as it
//depending on which the user was allowed to use.
switch (x){
case 1:
QuestionFile.open("Topic1 Questions.txt", ios::app);
break;
case 2:
QuestionFile.open("Topic2 Questions.txt", ios::app);
break;
case 3:
QuestionFile.open("Topic3 Questions.txt", ios::app);
break;
case 4:
QuestionFile.open("Topic4 Questions.txt", ios::app);
break;
}
if (!QuestionFile)
{
cout << "Cannot load file" << endl;
}
else
{
if (QuestionFile.peek() != ifstream::traits_type::eof()) {
while (!QuestionFile.eof())
{
//This simply gets which ever line you want and puts it into that part of the array
//and does this till the end of the array.
getline(QuestionFile, questions[i]);
i++;
}
QuestionFile.close();
}
}
Since you'd like to keep the switch statement, try this:
const char *filename;
size_t pos;
size_t oldPos;
stringstream buffer;
string test;
filename = NULL;
switch(x)
{
case 0:
filename = "Topic1 Questions.txt";
break;
case 1:
filename = "Topic2 Questions.txt";
break;
case 2:
filename = "Topic3 Questions.txt";
break;
case 3:
filename = "Topic4 Questions.txt";
break;
}
QuestionFile.open(filename, ios::app); // try opening the file
if(QuestionFile.is_open()) // if open succeeds...
{
buffer << QuestionFile.rdbuf();
test = buffer.str(); // read entire file into 'test'
pos = 0; // start at beginning
do
{
oldPos = pos;
pos = test.find("|", oldPos); // find position of next line
questions[i] = test.substr(oldPos, (pos - oldPos)); // put in array
i++;
}
while(pos != oldPos); // as long as pos2 changes.
QuestionFile.close();
}
else
{
cout << "Cannot load file" << endl;
}
Note: I have not allocated an array for you, so you still need to do this yourself.
I have a file of format as:
2
3 4
7 8 9
10 20 22 02
...
basically numbers in each line , separated by spaces.
I have to read from the file, extract all numbers and maintain their line number too, as I have to make a tree later. I'm doing this to take input, but getting weird outputs.
#include<cstdio>
#include<iostream>
#include<cctype>
using namespace std;
void input()
{
char c,p;
while(c=getchar()!=EOF)
{
if(c=='\n') printf("},\n{");
else if(c==' ') printf(",");
else if(c=='0')
{
p=getchar();
if(p==' ')
{
printf("%c%c,",c,p);
}
else
{
printf("%c,",p);
}
}
else if(isalpha(c))
{
printf("%c",c);
}
}
}
int main()
{
input();
}
The image shows the input and output
You are writing more C than C++.
In C++ you can use streams. Use peek() to check the next character, and >> to actually read it.
E.g.:
using namespace std;
int main(){
ifstream s("/tmp/input");
int nr;
while (!s.eof()) {
switch (s.peek()){
case '\n': s.ignore(1); cout << "},\n{"; break;
case '\r': s.ignore(1); break;
case ' ': s.ignore(1); cout << ", "; break;
default: if (s >> nr) cout << nr;
}
}
}
Use a file stream, read line by line and parse each line with a stringstream:
std::ifstream file("filename");
std::string line;
size_t line_number(1);
while ( std::getline(file, line) ) // reads whole lines until no more lines available
{
std::stringstream stream(line);
int tmp;
std::cout << "Numbers in line " << line_number << ":";
while ( stream >> tmp ) // reads integer divided by any whitespace until no more integers available
{
std::cout << " " << tmp;
}
std::cout << "\n";
++line_number;
}
You'll need to include
#include <iostream> // for std::cout
#include <string> // for std::string
#include <fstream> // for std::ifstream
#include <sstream> // for std::stringstream
I try to read in a binary file containing char and int & double after a header:
// open file
int pos = 0,n;
char str1,str2;
//string str;
ifstream fid(pfad.c_str(),std::ios::binary);
if (fid.good() != 1) {
printf(" ++ Error: The elegant bunch file %s doesn't exist.\n",pfad.c_str());
return 1;
}
// cut the header
while (pos<5) {
if (fid.eof()) {
printf(" ++ Error: elegant bunch file is strange\n");
return 1;
}
fid >> str1;
switch (pos) {
case 0: str2 = '&'; break;
case 1: str2 = 'd'; break;
case 2: str2 = 'a'; break;
case 3: str2 = 't'; break;
case 4: str2 = 'a'; break;
}
if (str1 == str2){
pos ++;
} else {
pos = 0;
}
}
// Read out the data
fid.seekg(19,ios_base::cur);
std::cout << fid.tellg() << std::endl;
fid >> n;
std::cout << fid.tellg() << std::cout;
printf("\n\n%i\n\n",n);
printf("\nOK\n");
return 0;
My reading char with fid >> str1 works just fine. If I try to do this with a int it produces somehow a strange behaviour. The output then gets
813
-10x6c4f0484
0
Whereby the first number is the position in the file and the second one should be the same, but it looks like a pointer to me. Can anybody maybe try to clarify me confusion?
Thanks already in advance.
std::operator>>(std::istream&, int&) tries to parse an integer from a stream of characters, it doesn't read binary data. You'll need to use the std::istream::read(char*, std::streamsize) function.