I'm trying to do something in C++ and I'm having a little bit of trouble figuring out exactly how to do it.
I have a text file that contains information that I need to parse and act on. The format of the file is multiple lines, each containing a number followed by a letter:
1234 A
5678 B
9101 C
What I need to do is, line by line, read the number and do a calculation based on it. Then I need to do an operation depending on the value of the letter on that line. Once both operations are finished, I repeat with the next line until all lines from the file have been parsed.
I've found some articles on how to read into strings line by line, but I can't quote figure out how to separate the lines into number and letter.
Any assistance is greatly appreciated!
The following example might help:
#include <iostream>
#include <fstream>
#include <string>
int main(int argc, char** argv)
{
std::ifstream fp("input.dat");
char ch;
int n;
while (fp >> n && fp >> ch)
{
std::cout << "Here is your number: " << n << std::endl;
std::cout << "Here is your char: " << ch << std::endl;
}
return 0;
}
In the above, the file input.dat contains your input. This example doesn't care if numbers and letters are separated by newlines, tabs, spaces or any other whitespace. If you really need to ready a particular format you could look into fscanf or std::getline (http://www.cplusplus.com/reference/string/string/getline/).
using namespace std;
ifstream file("file.txt");
int number;
char character;
while( file >> number >> character ) {
// read each line as a number followed by a character
cout << number << " " << character << endl;
switch( character ) {
case 'A':
break;
case 'B':
break;
case 'C':
break;
}
}
Related
I am trying to get the number of lines and words from a text file in c++. But one extra line is being read by compiler.
#include <iostream>
#include<fstream>
using namespace std;
int main(void)
{
ifstream f;
string name;
char a;
int line, words;
line = 0;
words = 0;
f.open("file.txt");
while (f) {
f.get(a);
if (a == '\n')
{
line++;
words++;
}
if (a == ' ')
{
words++;
}
}
f.close();
cout << "Number of words in file : " << words << endl;
cout << "Numbers of lines in the file : " << line << endl;
}
OUTPUT:-
Number of words in file : 79
Numbers of lines in the file : 3
file.txt:-
This C++ Program which counts the number of lines in a file. The program creates an input file stream, reads a line on every iteration of a while loop, increments the count variable and the count variable is printed on the screen.
Here is source code of the C++ program which counts the number of lines in a file. The C++ program is successfully compiled and run on a Linux system. The program output is also shown below.
I am puzzled why one extra line is being read. Kindly help.
You are not checking if f.get() succeeds or fails. When it does fail, a is not updated, and you are not breaking the loop yet, so you end up acting on a's previous value again. And then the next loop iteration detects the failure and breaks the loop.
Change this:
while (f) {
f.get(a);
...
}
to this instead:
while (f.get(a)) {
...
}
That being said, you are also not taking into account that the last line in a file may not end with '\n', and if it does not then you are not counting that line. And also, you are assuming that every line always has at least 1 word in it, as you are incrementing words on every '\n' even for lines that have no words in them.
I would suggest using std::getline() to read and count lines, and std::istringstream to read and count the words in each line, eg:
#include <fstream>
#include <sstream>
#include <string>
#include <cctype>
using namespace std;
int main(void)
{
ifstream f;
string line, word;
int lines = 0, words = 0;
f.open("file.txt");
while (getline(f, line))
{
++lines;
std::istringstream iss(line);
while (iss >> word) {
++words;
}
}
f.close();
cout << "Number of words in file : " << words << endl;
cout << "Numbers of lines in the file : " << line << endl;
}
It is because you do not check in what state is stream that f.get(a) returns.
i want to take a film name from user and change that to camel case , my code work if there is no numbers or spaces between letters
#include <iostream>
#include <string>
using namespace std;
int main()
{
int Count,Length=0;
string Films;
cout<<"Enter Film Count: ";
cin>>Count;
for(int i=0;i<Count;i++)
{
cout<<"Enter Film Names: ";
cin>>Films;
Length=0;
while(Length<1000)
{
switch(Length)
{
case 0: Films[Length]=toupper(Films[Length]); break;
default: Films[Length]=tolower(Films[Length]); break;
}
Length++;
}
cout<<"Results: "<<Films<<endl;
}
return 0;
}
i tried other topic solutions but i cant do it correctly.
Problem:
You've chosen the wrong approach to solve the problem. Your current code only changes the first character to uppercase and the rest to lowercase.
Solution:
Instead of using a while and a switch, use a for loop and an if statement that checks for spaces, delete them and change the following characters to uppercase.
Additional information:
using namespace std; is considered a bad practice (More info here).
The while loop can be replaced for a for loop to limit the Length scope and improve readability.
It's a good practice to check whether the std::cin inputs are valid or not to prevent Undefined Behavior.
Full code:
#include <iostream>
#include <string>
int main()
{
int count;
std::cout << "Enter film count: ";
std::cin >> count;
if(std::cin.fail())
{
std::cout << "Invalid input." << std::endl;
exit(0);
}
std::cin.ignore(10000,'\n');
for(int i = 0; i < count; i++)
{
std::string film;
std::cout << "Enter film name: ";
std::getline(std::cin, film);
if(std::cin.fail())
{
std::cout << "Invalid input." << std::endl;
exit(0);
}
if(film.size() == 0)
break;
film[0] = tolower(film[0]);
for(unsigned int i = 1; i < film.size() - 1; i++)
{
if(film[i] == ' ')
{
film.erase(i,1);
film[i] = toupper(film[i]);
i--;
}
}
std::cout << "Result: " << film << std::endl;
}
return 0;
}
Example:
Enter film count: 1
Enter file name: Love live! the school idol movie
Result: loveLive!TheSchoolIdolMovie
I could see really a lot of code to solve a simple problem. I will later show a one-liner that converts a string with words to camel case.
As a side note, code should always contain tons of comments. Otherwise, nobody will understand it, and later, even you will not understand your own code.
Anyway. Let us look at the requirements. What shall we do?
User shal input the number of film titles to convert
Title for title shall be read from the user
Titles have to converted to camel case style and shown to the user
Now, we think an how we want to solve the problem:
We will always instruct the user what to do next
We will make sanity checks for the users input
We will get the number of titles from the user
Then, we create a loop and read title for title
The current read title will be converted to camel case
The result will be shown to the user
Ok, we need to go into details for "The current read title will be converted to camel case"
Let us think again. We get a string, that consists of words. So, we need to extract words from the string. We consider that everything is a word, that is separated by white space.
Then, depending on the camle case style, please see here, we convert all first letters of a word to uppercase and discard all white spaces. The first word will have a starting lower case letter (selectable).
But how to extract words from a string? We remember the extractor operator >> will discard all white spaces and only read the text. That is what we need. So, we will pack the string into an std::istringstream and then extract word for word.
And with a simple boolean condition, we decide, if the first letter of the first word shall be in upper- or lower case.
So, let us implement or thoughts into code:
#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
int main() {
// Instruct user, what to do
std::cout << "\nPlease end number of film titles to convert: ";
// Read input from the user and check, if that was valid
if (size_t numberOfFilms{}; std::cin >> numberOfFilms && numberOfFilms > 0) {
// Now, in a loop, read all the film titles that the user wants to be processed
for (size_t index{}; index < numberOfFilms; ++index) {
// Instruct user, what to do
std::cout << "\n\nPlease end film title " << index + 1 << ": \t";
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(std::cin >> std::ws, line)) {
// Put the complete string into an istringstream, so that we can extract the words
std::istringstream lineStream{line};
// Here we can select the style of our camel case
bool wordShallHaveFirstLetterInUpperCase{ false };
// Extract all words from the line stream and convert first letter
for (std::string word{}; lineStream >> word; std::cout << word) {
// Depending on the camel case style
if (wordShallHaveFirstLetterInUpperCase)
word[0] = std::toupper(word[0]);
else
word[0] = std::tolower(word[0]);
// From now on all words shall start with an uppercase character
wordShallHaveFirstLetterInUpperCase = true;
}
}
else std::cerr << "\n\n*** Error: Problem while a title\n\n";
}
}
else std::cerr << "\n\n*** Error: Problem while reading the number of ilm titles\n\n";
return 0;
}
This is a rather straight forward implementation of our detailed design. And after running some tests, we see that it will work.
Now, for the more advanced users.
In professional software development, people try to avoid loops and branch statements. Because this will increase the code complexity (usually measured via the cyclomatic complexity). And complex code needs more tests cases for C0, C1 or even MCDC code coverage.
Therefore, often algorithms from the standard library are used. And they hide the loops somehow inside. But that is OK, because the standard library is thoroughly tested and sometimes even qualified with a certification.
So, as one example, you can do the whole camel case conversion with one statement. With std::transform and by using std::regex and iterators and a stateful Lambda.
The downside is, that it is not so easy to understand for the reader . . .
Please see yourself:
#include <iostream>
#include <iterator>
#include <algorithm>
#include <regex>
#include <string>
#include <cctype>
// The separator will be white space
const std::regex re{ R"(\s+)" };
int main() {
// Instruct user, what to do
std::cout << "\nPlease end number of film titles to convert: ";
// Read input from the user and check, if that was valid
if (size_t numberOfFilms{}; std::cin >> numberOfFilms && numberOfFilms > 0) {
// Now, in a loop, read all the film titles that the user wants to be processed
for (size_t index{}; index < numberOfFilms; ++index) {
// Instruct user, what to do
std::cout << "\n\nPlease end film title " << index+1 << ": \t";
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(std::cin >> std::ws, line)) {
// Convert to camel case and show output
std::transform(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::ostream_iterator<std::string>(std::cout),
[firstIsUpper = 0U](std::string s) mutable {if (firstIsUpper++) s[0] = std::toupper(s[0]); else s[0] = std::tolower(s[0]); return s; });
}
else std::cerr << "\n\n*** Error: Problem while a title\n\n";
}
}
else std::cerr << "\n\n*** Error: Problem while reading the number of ilm titles\n\n";
return 0;
}
Ultra compact, but difficult to read . . .
In case of questions, please ask.
Here is my code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
string nom_fich("data.dat");
ofstream fichier(nom_fich.c_str());
string name;
cout <<"The name is: "<< name ;
cin>>ws;
if (getline(cin, name)){
fichier << name <<endl;
} else {
cerr <<"Error!";
}
fichier.close();
return 0;
}
Question: why if I enter a number instead of a string my program doesn't say me "Error!" ?
EDIT: how can I attempt to my purpose ? I want that get an "Error!" when I enter a type that isn't a type string.
Because a number is a valid string.
Numbers can be represented as strings, i.e. strings can contain digit characters. For example:
std::string my_string("42");
std::cout << my_string[0]; // prints 4
std::cout << my_string[1]; // prints 2
You can't enter a number. You can enter a sequence of characters that can be interpreted as a number, but that sequence is still characters. Those characters arw what getline reads.
A number can be represented as a string, so std::ifstream::operator >> takes the intuitive approach: it treats any sequence of non-blank characters as a string. This includes decimal digits as well.
Unrelated, but instead of creating a superfluous nom_fich temporary variable for the name, you could just write ofstream fichier("data.dat");.
Because the string "123" is just as valid as the string "abc", or the string "def999".
As for "how can I attempt to my purpose?", you'd have to explain to us what your purpose is because, by your own admission, your own code does not describe that purpose and therefore we cannot extract your purpose from it.
Reading a number as a string will certainly work: the number is just represented as a sequence of characters. If you want the stream to attempt reading a number and fail if it doesn't get one, you'd use a different type to read, e.g.:
int number;
if (std::cin >> number) {
std::cout << "ERROR: read a number: " << number << '\n';
}
else if (std::cin.clear(), std::getline(std::cin, name)) {
std::cout << "read a name: " << name << '\n';
}
After the unsuccessful read of a number the stream's state is clear()ed and, instead, it is attempted to read a name. There is a subtle issue as the formatted input for an int will skip leading whitespace while std::getline() doesn't skip whitespace. If skipping leading whitespace is a problem just use the manipulator std::noskipws before trying to read an int.
I have a program which reads integers from a text file and skips non-integers and strange symbols. Then text file looks like:
# Matrix A // this line should be skipped because it contains # symbol
1 1 2
1 1$ 2.1 // this line should be skipped because it contains 2.1 and $
3 4 5
I have to print out the matrix without strange symbols and non-integers line. That is the output should be:
1 1 2
3 4 5
My code
ifstream matrixAFile("a.txt", ios::in); // open file a.txt
if (!matrixAFile)
{
cerr << "Error: File could not be opened !!!" << endl;
exit(1);
}
int i, j, k;
while (matrixAFile >> i >> j >> k)
{
cout << i << ' ' << j << ' ' << k;
cout << endl;
}
But it fails when it gets the first # symbol. Anyone helps please ?
Your problem is with this code.
int i, j, k;
while (matrixAFile >> i >> j >> k)
The assignment is "Find out if the line contains integers"
But your code is saying "I already know that the line contains integers"
If you are set to three integers per row, I suggest this pattern:
#include <fstream>
#include <sstream>
#include <string>
std::ifstream infile("matrix.txt");
for (std::string line; std::getline(infile, line); )
{
int a, b, c;
if (!(std::istringstream(line) >> a >> b >> c))
{
std::cerr << "Skipping unparsable line '" << line << "'\n";
continue;
}
std::cout << a << ' ' << b << ' ' << c << std::endl;
}
If the number of numbers per line is variable, you could use a skip condition like this:
line.find_first_not_of(" 0123456789") != std::string::npos
Of course, this fails at the # character: The # isn't an integer and, thus, reading it as an integer fails. What you could do is try to read three integers. If this fails and you haven't reached EOF (i.e., matrixAFile.eof() yields false, you can clear() the error flags, and ignore() everything up to a newline. The error recovery would look something like this:
matrixAFile.clear();
matrixAFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Note, that you need to bail out if you failed because eof() is true.
Since it's an assignment, I'm not giving full answer.
Read the data line by line to a string(call it str),
Split str into substrings,
In each substring, check if it's convertible to integer value.
Another trick is to read a line, then check that every char is between 0-9. It works if you don't need to consider negative numbers.
I think I'd just read a line at a time as a string. I'd copy the string to the output as long as it contains only digits, white-space and (possibly) -.
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');
}