9

I'm trying to move from stdio to iostream, which is proving very difficult. I've got the basics of loading a file and closing them, but I really don't have a clue as to what a stream even is yet, or how they work.

In stdio everything's relatively easy and straight forward compared to this. What I need to be able to do is

  1. Read a single character from a text file.
  2. Call a function based on what that character is.
  3. Repeat till I've read all the characters in the file.

What I have so far is.. not much:

int main()
{
    std::ifstream("sometextfile.txt", std::ios::in);
    // this is SUPPOSED to be the while loop for reading.  I got here and realized I have 
    //no idea how to even read a file
    while()
    {
    }
return 0;
}

What I need to know is how to get a single character and how that character is actually stored(Is it a string? An int? A char? Can I decide for myself how to store it?)

Once I know that I think I can handle the rest. I'll store the character in an appropriate container, then use a switch to do things based on what that character actually is. It'd look something like this.

int main()
{
    std::ifstream textFile("sometextfile.txt", std::ios::in);

    while(..able to read?)
    {
        char/int/string readItem;
        //this is where the fstream would get the character and I assume stick it into readItem?
        switch(readItem)
        {
        case 1:
            //dosomething
              break;
        case ' ':
            //dosomething etc etc
              break;
        case '\n':
        }
    }
return 0;
}

Notice that I need to be able to check for white space and new lines, hopefully it's possible. It would also be handy if instead of one generic container I could store numbers in an int and chars in a char. I can work around it if not though.

Thanks to anyone who can explain to me how streams work and what all is possible with them.

Jcrack
  • 289
  • 2
  • 4
  • 9
  • The method for reading a single "character" is [`get`](http://en.cppreference.com/w/cpp/io/basic_istream/get). As you can see it can be used to read more than one character as well. – Some programmer dude Feb 07 '12 at 13:18

6 Answers6

11

You also can abstract away the whole idea of getting a single character with streambuf_iterators, if you want to use any algorithms:

#include <iterator>
#include <fstream>

int main(){
  typedef std::istreambuf_iterator<char> buf_iter;
  std::fstream file("name");
  for(buf_iter i(file), e; i != e; ++i){
    char c = *i;
  }
}
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • After googling a little about iterators, I think this is the right way to go, but I still have a lot of research to do before I fully understand what's going on here. Thanks for the introduction to iterators. – Jcrack Feb 07 '12 at 16:35
8

You can also use standard for_each algorithm:

#include <iterator>
#include <algorithm>
#include <fstream>

void handleChar(const char& c)
{
    switch (c) {
        case 'a': // do something
            break;
        case 'b': // do something else
            break;
        // etc.
    }
}

int main()
{
    std::ifstream file("file.txt");
    if (file)
        std::for_each(std::istream_iterator<char>(file),
                      std::istream_iterator<char>(),
                      handleChar);
    else {
        // couldn't open the file
    }
}

istream_iterator skips whitespace characters. If those are meaningful in your file use istreambuf_iterator instead.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • 1
    Note that `istream_iterator` will construct a sentry for every token it reads IIRC, which in case of a single `char` can be quite the overhead. – Xeo Feb 08 '12 at 07:52
4

This has already been answered but whatever. You can use the comma operator to create a loop which behaves like a for each loop which goes through the entire file reads every character one by one and stop when it's done.

char c;
while((file.get(c), file.eof()) == false){
    /*Your switch statement with c*/
}

Explanation: The first part of the expression in the for loop (file.get(c), file.eof()) will function as follows. Firstly file.get(c) gets executed which reads a character and stores the result in c. Then, due to the comma operator, the return value is discarded and file.eof() gets executed which returns a bool whether or not the end of the file has been reached. This value is then compared.

Side Note: ifstream::get() always reads the next character. Which means calling it twice would read the first two character in the file.

batburger
  • 89
  • 6
1

fstream::get

Next time you have similar problem go to cplusplusreference or similar site, locate class you have problem with and read description of every method. Normally, this solves the problem. Googling also works.

SigTerm
  • 26,089
  • 6
  • 66
  • 115
0

I would honestly just avoid iterators here since it's just hurting readability. Instead, consider:

int main()
{
    std::ifstream file("sometextfile.txt")

    char c;
    while(file >> c) {
        // do something with c
    }
    // file reached EOF
    return 0;
}

This works because the stream implements operator bool, which makes it implicitly convertible to true if the stream hasn't reached EOF, and false if it has; and because file >> c returns the file itself, it can be used as the while condition.

Using an iterator is only really useful if you intend to use other functions from , but for plain reading, using the stream operator is simpler and easier to read.

Snaipe
  • 1,191
  • 13
  • 25
-2
while (textFile.good()) {
  char a;
  textFile.get(a);
   switch(a)
        {
        case 1:
            //dosomething
              break;
        case ' ':
            //dosomething etc etc
              break;
        case '\n':
    }
}
mikithskegg
  • 806
  • 6
  • 10
  • 2
    Make the read be the test in your read loops (http://stackoverflow.com/a/8558959/46642). – R. Martinho Fernandes Feb 07 '12 at 13:26
  • and don't declare automatic variables inside a loop – KevinDTimm Feb 07 '12 at 14:01
  • 1
    @KevinDTimm, I used to think earlier the same way, but having studied assembler code generated by modern compilators I saw that they are very clever and allocate memory for such variables BEFORE loop. – mikithskegg Feb 07 '12 at 15:39
  • @mikithskegg & @AProgrammer - though `modern compilers` are very clever I'm not willing to make the assumption that this is going to happen. Additionally, I believe the code I write should correctly imitate what I want to do (and I don't consider recreating a variable on every iteration of a loop to be what I want to do) – KevinDTimm Feb 07 '12 at 15:49
  • 2
    @KevinDTimm: I'd advise to concentrate on big picture instead. Optimal algorithms and code readability are top priority. The "measure before optimizing" principle is for everything else. – SigTerm Feb 07 '12 at 18:00
  • `Concentrate on big picture?` - like not write crappy code? And how does `optimal algorithms` not conflict with `measure before optimizing`? I started doing this when compilers weren't so smart and so, rather than unlearn good programming practices, I recommend these same practices to other people - because you never know when you're going to have to use a stupid compiler. – KevinDTimm Feb 07 '12 at 18:36