adriancortijo4
adriancortijo4

Reputation: 11

Comparing elements of text file

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

Answers (3)

David C. Rankin
David C. Rankin

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

A M
A M

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

Ted Lyngmo
Ted Lyngmo

Reputation: 117318

I decided to conver the whole file into an integer vector to be able to compare them.

You can then extract ints 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

Related Questions