Member Avatar for iamthwee

Ok let's say I have a text file:

(0,0) = 1.0	(0,1) = 2.0	(0,2) = 3.0	
(1,0) = g	(1,2) = 2.0	
(2,2) = hi there	(2,8) = ee	
(5,4) = e	
(10,1) = g

Where the each part of the line is delimited by a tab.

And the coords (x,y) represents the row and column in an excel file.

so (1,2) = 2.0 would mean put the value 2.0 on row 1 column 2 in the excel file. (or i should say row 2 column 3 because it starts at (0,0) but you get what I mean.)

How would I generate a csv file

1,2,3,,,,,,
g,,2,,,,,,
,,hi there,,,,,,ee


,,,,e,,,,




,g,,,,,,,

Note there are 8 commas per line because that is the maximum column value (highlighted in red).

Sample code would be great.

Do you think that this converter is an one-screen-page program?

Member Avatar for iamthwee

Do you think that this converter is an one-screen-page program?

What?

>How would I generate a csv file
The first thing that comes to mind is a sparse matrix to store the coordinates and value. Then when you iterate through the matrix, you can fill the gaps with empty fields and blank lines as appropriate, and really all you need to search for is the maximum number of fields:

int max_fields = 0;

// Find the largest column value
for ( int i = 0; i < matrix.size(); i++ ) {
  for ( int j = 0; j < matrix[i].size(); j++ ) {
    if ( matrix[i][j].column > max_fields )
      max_fields = matrix[i][j].column;
  }
}

int row = 0;

// Write out the CSV format
for ( int i = 0; i < matrix.size(); i++ ) {
  while ( row != matrix[i][0].row ) {
    new_csv_row();
    ++row;
  }

  int k = 0;

  for ( int j = 0; j <= max_fields; j++ ) {
    if ( k < matrix[i].size() && j == matrix[i][k].column )
      put_csv_field ( matrix[i][j].value );
    else
      put_csv_field ( "" );
  }

  new_csv_row();
}

Simple and effective.

As far as I know the empty CSV line is ",,,,,,,,".
With minimal syntax check up:

/* Includes:
#include <cstring>
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
*/
struct Tokenizer
{
public:
    Tokenizer(char delim):chdelim(delim)
    {}
    void setDelim(char delim);
    void clear() { token.clear(); }
    int parse(const char* ptext);
    int parse(const std::string& s) { return parse(s.c_str()); }
    int size() { return token.size(); }
    std::vector<std::string> token;
    char chdelim;
};

int Tokenizer::parse(const char* ptext)
{
    clear();
    if (!ptext)
        return 0;
    std::string tok;
    const char* p = ptext;
    const char* q, *qq;
    const char* qend = ptext + std::strlen(ptext);
    bool goon = true;

    for (p = ptext; goon ;p = q+1)
    {
        q = std::strchr(p,chdelim);
        if (!q)
        {
            q = qend;
            goon = false;
        }
        while (p < q && *p == ' ')
            ++p;
        for (qq = q - 1; qq > p && *qq == ' '; --qq)
            ;
        tok.assign(p,qq-p+1);
        token.push_back(tok);
    }
    return size();
}

struct RowCol
{
    RowCol():x(0),y(0) {}
    RowCol& set(int i, int j)
    {
        x = i; y = j;
        return *this;
    }
    bool parse(const char* pxy);
    bool parse(const std::string& sxy)
    {
        return parse(sxy.c_str());
    }
    int x, y;
};

bool operator <(const RowCol& a,const RowCol& b)
{
    return a.x < b.x?true:a.x > b.x?false:a.y < b.y;
}

bool RowCol::parse(const char* pxy)
{
    if (!pxy || *pxy != '(')
        return false;
    Tokenizer t(',');
    t.parse(pxy+1);
    if (t.size() != 2)
        return false;
    x = atoi(pxy+1);
    y = atoi(t.token[1].c_str());
    return true;
}

typedef std::map<RowCol,std::string> Map;
typedef Map::const_iterator Iter;
typedef std::pair<RowCol,std::string> V;

void CsvOut(const Map& m, int width, std::ostream& os)
{
    int xcurr = 0, ycurr = 0;
    
    for (Iter p = m.begin(); p != m.end(); ++p)
    {
        const RowCol& cell = (*p).first;
        if (cell.x != xcurr)
        {
            while (xcurr < cell.x)
            {
                for (int j = ycurr; j < width-1; ++j)
                    os << ',';
                os << '\n';
                ++xcurr;
                ycurr = 0;
            }
        }
        if (ycurr < cell.y)
        {
            while (ycurr < cell.y)
                ++ycurr,os << ',';
        }
        else if (cell.y)
            os << ',';
        os << (*p).second;
    }
    if (ycurr)
    {
        while (++ycurr < width)
            os << ',';
        os << '\n'; 
    }
    os.flush();
}

int CsvConverter(std::ostream& os, std::istream& is)
{
    Map m;
    RowCol xy;
    Tokenizer t('\t');
    Tokenizer u('=');
    std::string line;

    while (std::getline(is,line))
    {
        t.parse(line);
        for (int i = 0; i < t.size(); ++i)
        {
            u.parse(t.token[i]);
            if (u.size() == 2)
            {
                xy.parse(u.token[0]);
                m.insert(V(xy,u.token[1]));
            }
        }
    }
    CsvOut(m,9,os);
    return m.size();
}

const char* pTest =
    "(0,0) = 1.0\t(0,1) = 2.0\t(0,2) = 3.0\n"	
    "(1,0) = g\t(1,2) = 2.0\n"	
    "(2,2) = hi there\t(2,8) = ee\n"	
    "(5,4) = e\n"	
    "(10,1) = g\n"
    ;
int main(int argc, char* argv[])
{
    std::istringstream test(pTest);
    CsvConverter(std::cout,test);

    return 0;
}
/* Output:
1.0,2.0,3.0,,,,,,
g,,2.0,,,,,,
,,hi there,,,,,,ee
,,,,,,,,
,,,,,,,,
,,,,e,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,g,,,,,,,
*/
commented: Interesting, though I never thought that you of all people would use structs and methods together! @_@ +4

If everyone is going to give iamthwee free code, I might as well give my 2 cents :D

I've made some assumptions because your question wasn't complete:
- I went for excel, where a CSV is separated by ';' instead of ','
- an empty line is a '\n'
- there's no need to fill every line with semicolon's when there's no more data for that line
- your data never starts with a '(' (easy to change)
- the cell-coordinates will always be in ascending order in your input file.

Here's the code. It can be done better, but I don't have any time left to change/improve it :)

#include <iostream>
#include <string>
#include <map>
#include <fstream>
#include <sstream>

using namespace std;

int StringToInT(string str)
{
    int number;
    istringstream conv(str);
    conv >> number;
    return number;
}
int main()
{
    ifstream in("c:\\input.txt");
    if (!in.is_open())
    {
        cout << "open failed";
        return 1;
    }

    // put everything in a map, discarding whitespaces
    int counter = -1; //assuming '(' is first in file
    string buffer;
    map<int ,string> data;
    while (in >> buffer)
    {
        if (buffer[0] == '(') counter++;
        data[counter] += buffer + " ";
    }

    // find max col number
    int maxcols = 0;
    for (unsigned int i = 0; i < data.size(); i++)
    {
        string max = data[i].substr(data[i].find_first_of(",")+1
            ,data[i].find_first_of(")")- data[i].find_first_of(",")-1);
        int number = StringToInT(max);
        if (number > maxcols) maxcols = number;              
    }
    //write everything back
    ofstream out("c:\\output.csv");
    if (!out.is_open())
    {
        cout << "open failed";
        return 1;
    }
    int current_row =0;
    for (unsigned int j = 0; j < data.size(); j++)
    {
        string srow = data[j].substr(data[j].find_first_of("(")+1
            ,data[j].find_first_of(",")- data[j].find_first_of("(")-1);

        while (StringToInT(srow) != current_row)
        {
            out << "\n";
            current_row++;
        }
        for (int k = 0; k <=maxcols; k++)
        {
            string scol = data[j].substr(data[j].find_first_of(",")+1
                ,data[j].find_first_of(")")- 
                 data[j].find_first_of(",")-1);
            if (StringToInT(scol) == k)
            {
                out << data[j].substr(data[j].find_first_of("=")
                      +2,string::npos);
                break;
            }
            else 
                out << ";";
        }
    }
    return 0;
}

Output:

1.0 ;2.0 ;;3.0 
g ;;2.0 
;;hi there ;;;;;;;;ee 


;;;;e 




;g

(this is not the CSV standard, but works fine(better) in Excel)

[edit]
Come to think of it, my code adds an additional [space] to the data, don't have time to fix it now, sorry

Member Avatar for iamthwee

Change of plan, I'm using a completely different file now to parse through.

Change of plan, I'm using a completely different file now to parse through.

What? ;)

Were you just screwing with us, iamthwee? :icon_rolleyes:

Member Avatar for iamthwee

Were you just screwing with us, iamthwee? :icon_rolleyes:

No of course not. I am a very serious person. You have hurt my feelings.

Basically I am using another program/script which reads an excel file and dumps out the values.
The reason I am using this other script is because the values are more reliable and require less code for me to tamper with.


I am trying to write a free excel to tab/csv convertor.

To see this in context you might want to look at:
http://www.daniweb.com/forums/thread144178.html

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.