sikas
sikas

Reputation: 5523

C++ Read file line by line then split each line using the delimiter

I want to read a txt file line by line and after reading each line, I want to split the line according to the tab "\t" and add each part to an element in a struct.

my struct is 1*char and 2*int

struct myStruct
{
    char chr;
    int v1;
    int v2;
}

where chr can contain more than one character.

A line should be something like:

randomstring TAB number TAB number NL

Upvotes: 23

Views: 114210

Answers (4)

Parker
Parker

Reputation: 7494

I had some difficulty following some of the suggestions here, so I'm posting a complete example of overloading both input and output operators for a struct over a tab-delimited file. As a bonus, it also takes the input either from stdin or from a file supplied via the command arguments.

I believe this is about as simple as it gets while adhering to the semantics of the operators.


pairwise.h

#ifndef PAIRWISE_VALUE
#define PAIRWISE_VALUE

#include <string>
#include <iostream>

struct PairwiseValue
{
    std::string labelA;
    std::string labelB;
    float value;
};

std::ostream& operator<<(std::ostream& os, const PairwiseValue& p);

std::istream& operator>>(std::istream& is, PairwiseValue& p);

#endif

pairwise.cc

#include "pairwise.h"

std::ostream& operator<<(std::ostream& os, const PairwiseValue& p)
{
    os << p.labelA << '\t' << p.labelB << '\t' << p.value << std::endl;
    return os;
}

std::istream& operator>>(std::istream& is, PairwiseValue& p)
{
    PairwiseValue pv;

    if ((is >> pv.labelA >> pv.labelB >> pv.value))
    {
        p = pv;
    }

    return is;
}

test.cc

#include <fstream>
#include "pairwise.h"

int main(const int argc, const char* argv[])
{
    std::ios_base::sync_with_stdio(false); // disable synch with stdio (enables input buffering)

    std::string ifilename;
    if (argc == 2)
    {
        ifilename = argv[1];
    }

    const bool use_stdin = ifilename.empty();
    std::ifstream ifs;
    if (!use_stdin)
    {
        ifs.open(ifilename);

        if (!ifs)
        {
            std::cerr << "Error opening input file: " << ifilename << std::endl;
            return 1;
        }
    }

    std::istream& is = ifs.is_open() ? static_cast<std::istream&>(ifs) : std::cin;

    PairwiseValue pv;

    while (is >> pv)
    {
        std::cout << pv;
    }

    return 0;
}

Compiling

g++ -c pairwise.cc test.cc
g++ -o test pairwise.o test.o

Usage

./test myvector.tsv
cat myvector.tsv | ./test

Upvotes: 0

CashCow
CashCow

Reputation: 31435

Unless you intend to use this struct for C as well, I would replace the intended char* with std::string.

Next, as I intend to be able to read it from a stream I would write the following function:

std::istream & operator>>( std::istream & is, myStruct & my )
{
    if( std::getline(is, my.str, '\t') )
       return is >> my.v1 >> my.v2;
}

with str as the std::string member. This writes into your struct, using tab as the first delimiter and then any white-space delimiter will do before the next two integers. (You can force it to use tab).

To read line by line you can either continue reading these, or read the line first into a string then put the string into an istringstream and call the above.

You will need to decide how to handle failed reads. Any failed read above would leave the stream in a failed state.

Upvotes: 3

Loki Astari
Loki Astari

Reputation: 264381

Try:
Note: if chr can contain more than 1 character then use a string to represent it.

std::ifstream file("plop");
std::string   line;

while(std::getline(file, line))
{
    std::stringstream   linestream(line);
    std::string         data;
    int                 val1;
    int                 val2;

    // If you have truly tab delimited data use getline() with third parameter.
    // If your data is just white space separated data
    // then the operator >> will do (it reads a space separated word into a string).
    std::getline(linestream, data, '\t');  // read up-to the first tab (discard tab).

    // Read the integers using the operator >>
    linestream >> val1 >> val2;
}

Upvotes: 32

flownt
flownt

Reputation: 760

std::ifstream in("fname");
while(in){
    std::string line;
    std::getline(in,line);
    size_t lasttab=line.find_last_of('\t');
    size_t firsttab=line.find_last_of('\t',lasttab-1);
    mystruct data;
    data.chr=line.substr(0,firsttab).c_str();
    data.v1=atoi(line.substr(firsttab,lasttab).c_str());
    data.v2=atoi(line.substr(lasttab).c_str());
}

Upvotes: 2

Related Questions