Reputation: 11
I am trying to compare blocks of four numbers with each other to make a new output file with only the ones that meet that: four digit numbers which have all digits the same.
This is my code for the input file:
int main()
{
ofstream outfile ("text.txt");
outfile << "1111 1212 4444 \n 2222 \n \n 8888 4567" <<endl;
I want to split this in blocks of four like "1111", "1212" and so on to be able to only write the ones that meet the requirement in the new output file. I decided to conver the whole file into an integer vector to be able to compare them.
char digit;
ifstream file("text.txt");
vector <int> digits;
while(file>>digit)
{
digits.push_back(digit - '0');
}
and I suppose that the method that compares them would look something like this:
bool IsValid(vector<int> digits){
for (int i=0; i<digits.size() i++)
{
if(digits[0] == digits[1] == digits[2] == digits [3])
return true;
else
{
return false;
}
}
}
However this would just compare the first block, would you do it differently? or should I keep doing the vector idea.
Upvotes: 0
Views: 349
Reputation: 84569
Another approach is to simply handle each input as a string, and the loop over each character in the string validating that it is a digit and equal to the previous character. If it fails either test, then what was read wasn't an integer with all digits equal.
For example you could do:
#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
int main (void) {
int main (void) {
std::string s;
std::stringstream ss { "1 11 1111 foo 2222\nbar 1212\n4444\n8888\n4567\n"
"3433333 a8\n9999999999999999999\n" };
while (ss >> s) { /* read each string */
bool equaldigits = true; /* flags equal digits */
for (size_t i = 1; i < s.length(); i++) /* loop 1 - length */
/* validate previous & current digits & equal */
if (!isdigit(s[i-1]) || !isdigit(s[i]) || s[i-1] != s[i]) {
equaldigits = false; /* if not set flag false */
break; /* break loop */
}
/* handle empty-string or single char case */
if (!s.length() || (s.length() == 1 && !isdigit(s[0])))
equaldigits = false;
if (equaldigits) /* if all digits & equal */
std::cout << s << '\n'; /* output string */
}
}
The std::stringstream
above simply provides simulated input for the program.
(note: you can loop with std::string::iterator
if you like, or use a range-based for
loop and prev
char to store the last seen. Here, it's just as easy to iterate over indexes)
Using std::string find_first_not_of
Using existing string functions provides another way. After comparing that the first character is a digit, you can use std::basic_string::find_first_not_of to scan the rest of the string for a character that isn't the same as the first -- if the result isn't std::string::npos
, then your string isn't all the same digit.
#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
int main (void) {
std::string s;
std::stringstream ss { "1 11 1111 foo 2222\nbar 1212\n4444\n8888\n4567\n"
"3433333 a8\n9999999999999999999\n" };
while (ss >> s) { /* read each string */
if (!isdigit(s.at(0))) /* 1st char digit? */
continue;
/* if remainder of chars not equal 1st char - not equal digits */
if (s.find_first_not_of(s.at(0)) != std::string::npos)
continue;
std::cout << s << '\n';
}
}
Both approaches product the same output.
Example Use/Output
$ ./bin/intdigitssame
1
11
1111
2222
4444
8888
9999999999999999999
There are many other ways to do this as shown by the other good answers. It's worth understanding each approach.
Upvotes: 0
Reputation: 15267
Hm, all what I have seen is rather complicated.
Obviously you want to check for a pattern in a string. And patterns are usually matched with regular expressions.
This will give you an extremely short solution. Use std::regex
. Regular expressions are part of C++ standard library. And they are also easy to use. And for your case you the regex is (\d)\1{3}
. So, a digit followed by 3 of the same digits.
Program then boils down to one statement:
#include <sstream>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <string>
#include <regex>
std::istringstream testData{R"(1111 1212 444414 555
2222
8888 4567)"};
int main()
{
std::copy_if(
std::istream_iterator<std::string>(testData),
{},
std::ostream_iterator<std::string>(std::cout,"\n"),
[](const std::string& s){
return std::regex_match(s,std::regex(R"((\d)\1{3})"));
}
);
return 0;
}
Of course you may use any std::fstream
instead of the std::istringstream
And of course this is only one of many many possible and maybe not the best solution . . .
Upvotes: 1
Reputation: 117318
I decided to conver the whole file into an integer vector to be able to compare them.
You can then extract int
s from the stream directly (file >> int_variable
) and check if they are multiples of 1111 or not.
Suggestions in code:
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>
bool IsValid(int number) {
// Check that number is in the valid range and that it's a multiple of 1111.
return number >= 0 && number <= 9999 && (number / 1111) * 1111 == number;
}
// A function to process the values in a stream
std::vector<int> process_stream(std::istream& is) {
std::vector<int> digits;
int number;
while(is >> number) {
if(IsValid(number)) // Only save valid numbers
digits.push_back(number);
}
return digits;
}
int main() {
std::vector<int> digits;
// Check that opening the file succeeds before using it
if(std::ifstream file = std::ifstream("text.txt")) {
digits = process_stream(file);
}
// Print the collected int:s
for(int x : digits) {
std::cout << std::setw(4) << std::setfill('0') << x << '\n';
}
}
Upvotes: 0