2

I am quite new to C++ and I have a txt file with data which looks something like this:

test:123:lock

qwerty:4321:unlock

asdf:12:lock

Is it possible for me to read the data line by line into a variable / array using ":" as the delimiter?

I tried doing something like:

while(!myfile.eof()) {       
    for (int i = 0; i < 3; i++) {
        getline(myfile,UserN[i],':'); 
    }
}

What I want to achieve is to store the data of the first line into the UserN[0], UserN[1], and UserN[2]. And when it start reading the second line, the data on the second line will replace the value in UserN[0], UserN[1], and UserN[2]. Thanks in advance!

leemes
  • 44,967
  • 21
  • 135
  • 183
1cyf1r3
  • 53
  • 1
  • 1
  • 9
  • strtok is C, but would help here – Grantly Jan 19 '15 at 13:04
  • 2
    You should [never use `eof`](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong). – molbdnilo Jan 19 '15 at 13:08
  • 1
    Why do you want to read the second line into the same variables than the first line? Do you instead mean a two-dimensional array? – leemes Jan 19 '15 at 13:10
  • I can see what you have tried, and... What was the result of trying that? What went wrong? – Raydel Miranda Jan 19 '15 at 13:22
  • a program like 'while(!EOF){...}' is always wrong. If an read error occurs the fail flag is set, but not the EOF-flag. So You get an endless loop. – cpp-progger Jan 19 '15 at 15:01

3 Answers3

1

Read the line first, then tokenize it with std::stringstream:

#include <sstream>

...

std::string line;

while(std::getline(myfile, line)) {       // cache the line
  std::istringstream tokenizer(line);

  std::getline(tokenizer, UserN[0], ':'); // then get the tokens from it
  std::getline(tokenizer, UserN[1], ':');
  std::getline(tokenizer, UserN[2]);      // last token: get the remainder
                                          // of the line.

  if(tokenizer) {
    // success!
  } else {
    // There were fewer than two colons in the line
  }
}

In essence, std::istringstream wraps a string in a stream interface -- the resulting stream behaves (roughly) like a file with the same contents as the string with which it was built. It is then possible to use >> or getline or anything else that you could use on files or std::cin or other input streams with it, and here we use it to take the string apart into the tokens you require.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
0

You can do this simply with

ifstream myfile( "aFile.txt" );
// .. check whether the file is open: if( !myfile.is_oppen() ) error
for( string userN[3]
    ; getline( getline( getline( myfile >> ws, userN[0], ':' ), userN[1], ':' ), userN[2] ); )
{
    // userN[0..2] is read correctly
}

or in a more elegant way, perhaps more suitable to Your requirements. I assume, that the second text is always a number and the third text is either 'lock' or 'unlock' or something else like an enum.

enum class LockState 
{ 
    lock, unlock 
};

// --   reading a LockState
//      please consider, that behind the text must follow a white space character (Space, LF, ..)
std::istream& operator>>(std::istream& in, LockState& s)
{
    std::string word;
    if( in >> word )
    {
        if( word == "lock" )
            s = LockState::lock;
        else if( word == "unlock" )
            s = LockState::unlock;
        else
            in.setstate( std::ios_base::failbit );
    }
    return in;
}

struct Entry    // change the name 'Entry' of the struct suitable for Your requirements
{
    std::string someText;
    int aNr;
    LockState lockState;
};

// --   function to read an 'Entry'-object
std::istream& operator>>(std::istream& in, Entry& e)
{
    char colon;
    if( getline( in >> std::ws, e.someText, ':' ) >> e.aNr >> colon
        && colon != ':' )
        in.setstate( std::ios_base::failbit );
    else
        in >> e.lockState;
    return in;
}

and later in Your main-program

ifstream myfile( "aFile.txt" );
// .. check whether the file is open: if( !myfile.is_oppen() ) error
for( Entry e; myfile >> e; )
{
    // use here the Entry-object 'e'
}
if( myfile.eof() )
    cout << "Ok - You read the file till the end" << endl;
cpp-progger
  • 406
  • 3
  • 6
0

Avoid trouble here and use the split function from Boost:

#include <fstream>
#include <vector>
#include <string>
#include <boost/algorithm/string.hpp>

// ...

// Read file and throw exception on error.
std::ifstream infile;
infile.open(file_name);

std::string line;

while (std::getline(infile, line))
{
    // Strip of the comments.
    std::vector<std::string> strings;
    boost::split(strings, line, boost::is_any_of(":"));

    // You have now a vector of strings, which you can process...
}
Chiel
  • 6,006
  • 2
  • 32
  • 57