23

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
Arun
  • 19,750
  • 10
  • 51
  • 60
sikas
  • 5,435
  • 28
  • 75
  • 120
  • 7
    a char cannot contain more than one character, you probably mean char* also the purpose of v1 and 2 is not entirely clear to me. – flownt Oct 11 '10 at 22:16
  • 2
    @Mark he's probably bussy editing... – flownt Oct 11 '10 at 22:20
  • Any code you can show us? Can you tell us what `v1` and `v2` are? Are there are constraints? – wkl Oct 11 '10 at 22:22
  • @Mark Rushakoff: I want to read the file line by line. When I read the first line, I want to split it using the tab as a delimiter then each part of the line is set to a variable in the struct. Next move to next line till eof. – sikas Oct 11 '10 at 22:24
  • @sikas so a line should be something like : randomstring TAB number TAB number NL ? – flownt Oct 11 '10 at 22:25
  • @flownt: yes, that is the format of each line in the file. So how can I read each line, split and add to struct? – sikas Oct 11 '10 at 22:26
  • 1
    @sikas: Okay, you have the pseudocode all worked out. So what's stopping you from translating it to C++? – Mark Rushakoff Oct 11 '10 at 22:26
  • @Mark: I have the pseudocode, yes. But can`t apply!! I haven`t written any code in C++ for nearly 2 yrs so I kinda forgot all about it :S ... – sikas Oct 11 '10 at 22:30
  • Any help would be appreciated! – sikas Oct 11 '10 at 22:39
  • 2
    @sikas Then now would be a great time to re-learn C++. Otherwise, are you just expecting someone here to do all the work for you? – TheUndeadFish Oct 11 '10 at 23:12

4 Answers4

32

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;
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
3

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.

CashCow
  • 30,981
  • 5
  • 61
  • 92
2
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());
}
flownt
  • 760
  • 5
  • 11
0

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
Parker
  • 7,244
  • 12
  • 70
  • 92