I want to replace hi with a bye by reading a file and outputting another file with the replaced letters.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream myfile;
ofstream output;
output.open("outputfile.txt");
myfile.open("infile.txt");
char letter;
myfile.get(letter);
while (!myfile.eof()) {
if (letter == 'H') {
char z = letter++;
if (z == 'i')
output << "BYE";
}
else output << letter;
}
output.close();
myfile.close();
return 0;
}
My outputs are repeated capital I's that is repeated infinity times.
Here is my input file
Hi
a Hi Hi a
Hi a a Hi
Don't check eof
The eof method is returning the location of the input stream read pointer, and not the status of the get. It is more like telling you whether or not get will succeed, so you could write something like:
while (!myfile.eof()) {
char letter;
myfile.get(letter);
//...
}
In this way, you would at least be getting a new letter at each iteration, and the loop ends when the read pointer reaches the end of the input.
But, there are other cases that might cause the get to not succeed. Fortunately, these are captured by the stream itself, which is returned by get. Testing the status of the stream is as easy as treating the stream as a boolean. So, a more idiomatic way to write the loop is:
char letter;
while (myfile.get(letter)) {
//...
}
Peek at the next letter
When you want to look at the next letter in the input following the detected 'H', you perform an increment.
char z = letter++;
But, this does not achieve the desired result. Instead, it just sets both letter and z variables to the numerical successor of 'H' ('H' + 1), and does not observe the next letter in the input stream.
There is another method you can use that is like get, but leaves the input in the input stream. It is called peek.
char z;
auto peek = [&]() -> decltype(myfile) {
if (myfile) z = myfile.peek();
return myfile;
};
if (peek()) {
//...
}
And now, you can check the value of z, but it is still considered input for the next get on letter.
Close to what you implemented
So, the complete loop could look like:
char letter;
while (myfile.get(letter)) {
if (letter == 'H') {
char z;
auto peek = [&]() -> decltype(myfile) {
if (myfile) z = myfile.peek();
return myfile;
};
if (peek() && z == 'i') {
myfile.get(z);
output << "BYE";
continue;
}
}
output << letter;
}
With this approach, you will be able to correctly handle troublesome cases like HHi as input, or the last letter in the input being an H.
Your two lines:
myfile.get(letter);
while (!myfile.eof()) {
are wrong.
First off you only read letter once, hence your infinite loop.
Secondly you don't use eof in a while loop.
You want something more like:
while (myfile.get(letter)) {
Also:
char z = letter++;
is wrong, you want to read another letter:
myfile.get(z);
but you have to be careful that you get something, so
if(!myfile.get(z)) {
output << letter;
break;
}
So finally:
char letter;
while (myfile.get(letter)) {
if (letter == 'H') {
char z;
if(!myfile.get(z)) {
output << letter;
break;
}
if (z == 'i') {
output << "BYE";
}
else output << letter << z;
}
else output << letter;
}
But now we are consuming the character after any H which may not be desirable.
See #jxh's answer for a way to do this with look ahead.
There is a dedicated function to replace patterns in strings. For example, you could use std::regex_replace. That is very simple. We define, what should be searched for and with what that would be replaced.
Some comments. On StackOverflow, I cannot use files. So in my example program, I use a std::istringstream instead. But this is also an std::istream. You can use any other std::istream as well. So if you define an std::ifstream to read from a file, then it will work in the same way as the std::istringstream. You can simply replace it. For the output I use the same mechanism to show the result on the console.
Please see the simple solution:
#include <iostream>
#include <sstream>
#include <regex>
// The source file
std::istringstream myfile{ R"(Hi
a Hi Hi a
Hi a a Hi)" };
// The destination file
std::ostream& output{ std::cout };
int main() {
// Temporary string, to hold one line that was read from a file
std::string line{};
// Read all lines from the file
while (std::getline(myfile, line)) {
// Replace the sub-string and write to output file
output << std::regex_replace(line, std::regex("Hi"), "Bye") << "\n";
}
return 0;
}
Related
So I have data in a text like this:
Alaska 200 500
New Jersey 400 300
.
.
And I am using ifstream to open it.
This is part of a course assignment. We are not allowed to read in the whole line all at once and parse it into the various pieces. So trying to figure out how to read each part of every line.
Using >> will only read in "New" for "New Jersey" due to the white space/blank in the middle of that state name. Have tried a number of different things like .get(), .read(), .getline(). I have not been able to get the whole state name read in, and then read in the remainder of the numeric data for a given line.
I am wondering whether it is possible to read the whole line directly into a structure. Of course, structure is a new thing we are learning...
Any suggestions?
Can't you just read the state name in a loop?
Read a string from cin: if the first character of the string is numeric then you've reached the next field and you can exit the loop. Otherwise just append it to the state name and loop again.
Here is a line by line parsing solution that doesn't use any c-style parsing methods:
std::string line;
while (getline(ss, line) && !line.empty()) {
size_t startOfNumbers = line.find_first_of("0123456789");
size_t endOfName = line.find_last_not_of(" ", startOfNumbers);
std::string name = line.substr(0, endOfName); // Extract name
std::stringstream nums(line.substr(startOfNumbers)); // Get rest of the line
int num1, num2;
nums >> num1 >> num2; // Read numbers
std::cout << name << " " << num1 << " " << num2 << std::endl;
}
If you can't use getline, do it yourself: Read and store in a buffer until you find '\n'. In this case you probably also cannot use all the groovy stuff in std::string and algorithm and might as well use good ol' C programming at that point.
Once you have grabbed a line, read your way backwards from the end of the line and
Discard all whitespace until you find non whitespace.
Gather characters found into token 3 until you find whitepace again.
Read and discard the whitespace until you find the end of token 2.
Gather token 2 until you find more whitespace.
Discard the whitespace until you find the end of token 1. The rest of the line is all token 1.
convert token 2 and token 3 into numbers. I like to use strtol for this.
You can build all of the above or Daniel's answer (use his answer if at all possible) into an overload of operator>>. This lets you
mystruct temp;
while (filein >> temp)
{
// do something with temp. Stick it in a vector, whatever
}
The code to do this looks something like (Stealing wholesale from What are the basic rules and idioms for operator overloading? <-- Read this. It could save your life one day)
std::istream& operator>>(std::istream& is, mystruct & obj)
{
// read obj from stream
if( /* no valid object of T found in stream */ )
is.setstate(std::ios::failbit);
return is;
}
Here's another example of reading the file word by word. Edited to remove the example using the eof check as the while loop condition. Also included a struct as you mentioned that's what you just learned. I'm not sure how you're supposed to use your struct, so I just made it simple and had it contain 3 variables, a string, and 2 ints. To verify it reads correctly it couts the contents of the struct variables after its read in which includes printing out "New Jersey" as one word.
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h> // for atoi
using namespace std;
// Not sure how you're supposed to use the struct you mentioned. But for this example it'll just contain 3 variables to store the data read in from each line
struct tempVariables
{
std::string state;
int number1;
int number2;
};
// This will read the set of characters and return true if its a number, or false if its just string text
bool is_number(const std::string& s)
{
return !s.empty() && s.find_first_not_of("0123456789") == std::string::npos;
}
int main()
{
tempVariables temp;
ifstream file;
file.open("readme.txt");
std::string word;
std::string state;
bool stateComplete = false;
bool num1Read = false;
bool num2Read = false;
if(file.is_open())
{
while (file >> word)
{
// Check if text read in is a number or not
if(is_number(word))
{
// Here set the word (which is the number) to an int that is part of your struct
if(!num1Read)
{
// if code gets here we know it finished reading the "string text" of the line
stateComplete = true;
temp.number1 = atoi(word.c_str());
num1Read = true; // won't read the next text in to number1 var until after it reads a state again on next line
}
else if(!num2Read)
{
temp.number2 = atoi(word.c_str());
num2Read = true; // won't read the next text in to number2 var until after it reads a state agaon on next line
}
}
else
{
// reads in the state text
temp.state = temp.state + word + " ";
}
if(stateComplete)
{
cout<<"State is: " << temp.state <<endl;
temp.state = "";
stateComplete = false;
}
if(num1Read && num2Read)
{
cout<<"num 1: "<<temp.number1<<endl;
cout<<"num 2: "<<temp.number2<<endl;
num1Read = false;
num2Read = false;
}
}
}
return 0;
}
The following code is meant to take all floating numbers for calculation from the file "float.txt" The problem is the floating numbers has junk information between it.
example file:
23.5 aujsaN8.2<:::32
After the first floating point is gotten, the while loop never ends, the program no longer gets any information from the file. please help.
int main()
{
float num;
ifstream input("float.txt");
input >> num;
while (!(input.eof()))
{
input >> num;
}
input.close();
return 0;
}
You could do something like this:
decltype(input)::Traits::int_type c;
while ((c = input.peek()) != decltype(input)::Traits::eof())
{
if (std::isdigit(c))
{
input >> num;
... use num ...
}
else
input.get();
}
The idea here is to peek for the next character, and if it's a digit then we know a >> streaming to double will succeed, otherwise we actually get() the character to remove it from the input stream.
It gets trickier if you need to extract negative numbers (hint: one way - use a bool to track if the last value of c seen was -, then have if (the_bool) num = -num; before using num). While the code above handles e.g. X0.23X -> 0.23, you may need or may not need to also handle X.23X - if so, check for . then see if the next character is a digit... the tricky thing is that peeking for the digit means you've already consumed the ., so it won't be there for input >> num... you can try input.putback('.') but I'm not certain it's Standard-required to work when you've consumed a character then peeked - you'd have to check....
Look i don't know how the statement input>>num; works i never used those, instead i will do these in order to extract floats from your specified file.
float floats=0;
char string[100], ch;
while (file.get(ch)!=' ')
{
string[i]=ch;
i++;
}
string[i]='\0';
floats=atof(string);
This program simply copies the characters untill a ' ' (Space) is found, like the file u shown, then the function aotf() converts the string to floating point number.
Is this the answer to your question??, if yes then please vote +1, and if any question u can ask me, i will sure help u...
this works
// g++ -o parse_float_file parse_float_file.cpp -std=c++11
#include <iostream>
#include <fstream>
#include <string>
int main() {
float curr_number;
std::ifstream inFile("float.txt");
std::string line;
while(getline(inFile, line)) {
try {
curr_number = std::stof(line);
std::cout << "-->" << curr_number << "<--" << std::endl;
} catch (const std::exception &e) {
std::cout << "ERROR - not a float : " << line << std::endl;
}
}
return 0;
}
I want to read from a file character by character and perform a certain operation on every character I am using the following loop:
ifstream in
while(in)
{
ch=in.get();
//some operation
}
I don't want to read the character in condition for while because then cursor will move to next position and I'll miss that character.
The problem is that the end of the file is not properly signalled and the last character is read two times. Please give a way to avoid this
Eg. if The string in the file is
army
it is read as armyy (when I print)
char ch;
while(in.get(ch)){ } //or in>>std::noskipws>>c
Would be the proper way as the character you want is stored in ch. what is the problem with that?
If you really want it the way you want, then you may use peek() to see the next character and perform appropriate opeartion as:
char c = in.peek(); //this will give you the next character in the stream
//if its an eof, do appropriate
Use the other overload of get:
while (in.get(ch)) {
// do something with ch
}
or
for (char ch; in.get(ch); ) {
// do something with ch
}
You can also use sscanf for reading char.. In that example you can see that 3 input are reading from text . First two are string , last is float .. And also you can use a vector to store values..
Hope this example can be helpfull
std::string str;
char buf_1[50];
char buf_2[50];
while(std::getline(in, str))
{
if(sscanf(str.c_str(), "%s %s %f", buf_1, buf_2, &faceStatistics.statistics) == 3)
{
faceStatistics.faceName_1 = buf_1;
faceStatistics.faceName_2 = buf_2;
faceStat_.push_back(faceStatistics);
}
else
std::cout << "No param in string " << str << std::endl;
}
vector assign
struct Fstat {
std::string faceName_1;
std::string faceName_2;
float statistics;
};
I was wondering if someone could help me figure out how to read from a text file in C++, character by character. That way, I could have a while loop (while there's still text left) where I store the next character in the text document in a temp variable so I could do something with it, then repeat the process with the next character. I know how to open the file and everything, but temp = textFile.getchar() doesn't seem to work.
You could try something like:
char ch;
fstream fin("file", fstream::in);
while (fin >> noskipws >> ch) {
cout << ch; // Or whatever
}
#cnicutar and #Pete Becker have already pointed out the possibility of using noskipws/unsetting skipws to read a character at a time without skipping over white space characters in the input.
Another possibility would be to use an istreambuf_iterator to read the data. Along with this, I'd generally use a standard algorithm like std::transform to do the reading and processing.
Just for example, let's assume we wanted to do a Caesar-like cipher, copying from standard input to standard output, but adding 3 to every upper-case character, so A would become D, B could become E, etc. (and at the end, it would wrap around so XYZ converted to ABC.
If we were going to do that in C, we'd typically use a loop something like this:
int ch;
while (EOF != (ch = getchar())) {
if (isupper(ch))
ch = ((ch - 'A') +3) % 26 + 'A';
putchar(ch);
}
To do the same thing in C++, I'd probably write the code more like this:
std::transform(std::istreambuf_iterator<char>(std::cin),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(std::cout),
[](int ch) { return isupper(ch) ? ((ch - 'A') + 3) % 26 + 'A' : ch;});
Doing the job this way, you receive the consecutive characters as the values of the parameter passed to (in this case) the lambda function (though you could use an explicit functor instead of a lambda if you preferred).
To quote Bjarne Stroustrup:"The >> operator is intended for formatted input; that is, reading objects of an expected type and format. Where this is not desirable and we want to read charactes as characters and then examine them, we use the get() functions."
char c;
while (input.get(c))
{
// do something with c
}
Here is a c++ stylish function your can use to read files char by char.
void readCharFile(string &filePath) {
ifstream in(filePath);
char c;
if(in.is_open()) {
while(in.good()) {
in.get(c);
// Play with the data
}
}
if(!in.eof() && in.fail())
cout << "error reading " << filePath << endl;
in.close();
}
//Variables
char END_OF_FILE = '#';
char singleCharacter;
//Get a character from the input file
inFile.get(singleCharacter);
//Read the file until it reaches #
//When read pointer reads the # it will exit loop
//This requires that you have a # sign as last character in your text file
while (singleCharacter != END_OF_FILE)
{
cout << singleCharacter;
inFile.get(singleCharacter);
}
//If you need to store each character, declare a variable and store it
//in the while loop.
Re: textFile.getch(), did you make that up, or do you have a reference that says it should work? If it's the latter, get rid of it. If it's the former, don't do that. Get a good reference.
char ch;
textFile.unsetf(ios_base::skipws);
textFile >> ch;
Assuming that temp is a char and textFile is a std::fstream derivative...
The syntax you're looking for is
textFile.get( temp );
There is no reason not to use C <stdio.h> in C++, and in fact it is often the optimal choice.
#include <stdio.h>
int
main() // (void) not necessary in C++
{
int c;
while ((c = getchar()) != EOF) {
// do something with 'c' here
}
return 0; // technically not necessary in C++ but still good style
}
I wrote the code below that successfully gets a random line from a file; however, I need to be able to modify one of the lines, so I need to be able to get the line character by character.
How can I change my code to do this?
Use std::istream::get instead of std::getline. Just read your string character by character until you reach \n, EOF or other errors. I also recommend you read the full std::istream reference.
Good luck with your homework!
UPDATE:
OK, I don't think an example will hurt. Here is how I'd do it if I were you:
#include <string>
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
static std::string
answer (const string & question)
{
std::string answer;
const string filename = "answerfile.txt";
ifstream file (filename.c_str ());
if (!file)
{
cerr << "Can't open '" << filename << "' file.\n";
exit (1);
}
for (int i = 0, r = rand () % 5; i <= r; ++i)
{
answer.clear ();
char c;
while (file.get (c).good () && c != '\n')
{
if (c == 'i') c = 'I'; // Replace character? :)
answer.append (1, c);
}
}
return answer;
}
int
main ()
{
srand (time (NULL));
string question;
cout << "Please enter a question: " << flush;
cin >> question;
cout << answer (question) << endl;
}
... the only thing is that I have no idea why do you need to read string char by char in order to modify it. You can modify std::string object, which is even easier. Let's say you want to replace "I think" with "what if"? You might be better off reading more about
std::string and using find, erase, replace etc.
UPDATE 2:
What happens with your latest code is simply this - you open a file, then you get its content character by character until you reach newline (\n). So in either case you will end up reading the first line and then your do-while loop will terminate. If you look into my example, I did while loop that reads line until \n inside a for loop. So that is basically what you should do - repeat your do-while loop for as many times as many lines you want/can get from that file. For example, something like this will read you two lines:
for (int i = 1; i <= 2; ++i)
{
do
{
answerfile.get (answer);
cout << answer << " (from line " << i << ")\n";
}
while (answer != '\n');
}