Reading file line by line WITHOUT using FOR/WHILE

2019-09-06 13:17发布

Hey guys here's a interesting challenge for you all. I'm given a text file and I'm supposed to process the information line by line. The processing part is trivial so long as I can obtain the individual lines. However here's the challenge:

  1. I must do so WITHOUT using any FOR/WHILE loops in my code. (This include recursions)
  2. I am only allowed to use the standard C++ library.

Currently right now my best solution is this: Is there a C++ iterator that can iterate over a file line by line? but I'm hoping for a better one that does not involve creating my own iterator class or implementing a proxy for std::string.

P.S. this is for a school assignment and the challenge here was to solve the problem using a combination of std functionalities and algorithms but I have no clue how to go about solving it

2条回答
贼婆χ
2楼-- · 2019-09-06 13:47
ifstream input("somefile")

if (!input) { /* Handle error */ }

//MyDataType needs to implement an operator>>

std::vector<MyDataType> res;

std::istream_iterator<MyDataType> first(input);
std::istream_iterator<MyDataType> last;
std::copy(first,last, std::back_inserter(res));

//etc..

Your input operator can be something like this:

std::istream& operator>>(std::istream &in,MyDataType & out)
{
    std::string str;
    std::getline(in,str);
    //Do something with str without using loops
    return in;
}

There are a lot of loops here (you dont' want to use goto, don't you?), but they are all hidden behind std::copy and std::getline

查看更多
Luminary・发光体
3楼-- · 2019-09-06 13:55

This is just a toy.

It consists of a Generator based way to slurp up files (which I find easy to write), together with a half-working adapter that turns Generators into Iterators.

A Generator in this context is a functor that takes a lambda, and passes that lambda the thing iterated over and returns true, or returns false. It makes a really short loop:

while( gen( [&]( std::string line ) {
  // code using line goes here
} ) ); // <- notice that this is an empty while loop that does stuff

but you aren't allowed to use that. (This design is inspired by python generators, which I honestly find much more natural to work with than C++ iterators for many kinds of problem, which includes reading from a file).

The adpator takes this generator and makes it an iterator, so you can pass it to for_each.

The point of this is that your question itself is inane, so rather than do something obvious (goto, or directly use for_each on an istream iterator), I'm proposing messing around with something obtuse and different. :)

The Line generator is nice and short:

struct LineGenerator {
  LineGenerator(std::ostream& os_):os(&os_) {}
  std::ostream* os;
  template<typename F>
  bool operator()(F&& func) {
    std::string s;
    bool bRet = std::getline( *os, s );
    if (!bRet) return false;
    std::forward<F>(func)(s);
    return true;
  }
};

The adaptor, on the other hand, is pretty messy: (and not tested)

template<typename Generator>
struct generator_iterator {
  typedef generator_iterator<Generator> my_type;
  typedef typename std::decay< decltype( std::declval<Generator>()() ) >::type value_type;
  Generator g;
  bool bDone;
  value_type val;
  generator_iterator( Generator g_ ):g(g_), bDone(false) {
    next();
  }
  generator_iterator(): bDone(true) {}
private:
  void next() {
    if (bDone) return;
    bDone = g(val);
  }
public:
  generator_iterator& operator++() {
    next();
    return *this;
  }
  value_type operator*() {
    return val;
  }
  const value_type operator*() const {
    return val;
  }
  value_type* operator->() {
    return &val;
  }
  value_type const* operator->() const {
    return &val;
  }
  bool operator==( my_type const& o ) {
    if (bDone != o.bDone) return false;
    if (!bDone && !o.bDone) return true;
    return false;
  }
};
template<typename Generator>
generator_iterator<Generator> make_gen_it( Generator&& g ) {
  return generator_iterator<Generator>( std::forward<Generator>(g) );
}
template<typename Generator>
generator_iterator<Generator> make_end_gen_it() {
  return generator_iterator<Generator>();
}

I hope this amuses!

查看更多
登录 后发表回答