Task: CSV data manipulation


C++: #include <map>
#include <vector>
#include <iostream>
#include <fstream>
#include <utility>
#include <functional>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>

class CSV
{
public:
    CSV(void) : m_nCols( 0 ), m_nRows( 0 )
    {}

    bool open( const char* filename, char delim = ',' )
    {
        std::ifstream file( filename );
    
        clear();
        if ( file.is_open() )
        {
            open( file, delim );
            return true;
        }

        return false;
    }

    void open( std::istream& istream, char delim = ',' )
    {
        std::string         line;

        clear();
        while ( std::getline( istream, line ) )
        {
            unsigned int nCol = 0;
            std::istringstream    lineStream(line);
            std::string           cell;

            while( std::getline( lineStream, cell, delim ) )
            {
                m_oData[std::make_pair( nCol, m_nRows )] = trim( cell );
                nCol++;
            }
            m_nCols = std::max( m_nCols, nCol );
            m_nRows++;
        }
    }

    bool save( const char* pFile, char delim = ',' )
    {
        std::ofstream ofile( pFile );
        if ( ofile.is_open() )
        {
            save( ofile );
            return true;
        }
        return false;
    }

    void save( std::ostream& ostream, char delim = ',' )
    {
        for ( unsigned int nRow = 0; nRow < m_nRows; nRow++ )
        {
            for ( unsigned int nCol = 0; nCol < m_nCols; nCol++ )
            {
                ostream << trim( m_oData[std::make_pair( nCol, nRow )] );
                if ( (nCol+1) < m_nCols )
                {
                    ostream << delim;
                }
                else
                {
                    ostream << std::endl;
                }
            }
        }
    }

    void clear()
    {
        m_oData.clear();
        m_nRows = m_nCols = 0;
    }

    std::string& operator()( unsigned int nCol, unsigned int nRow )
    {
        m_nCols = std::max( m_nCols, nCol+1 );
        m_nRows = std::max( m_nRows, nRow+1 );
        return m_oData[std::make_pair(nCol, nRow)];
    }

    inline unsigned int GetRows() { return m_nRows; }
    inline unsigned int GetCols() { return m_nCols; }

private:
    
    inline std::string &trim(std::string &s) 
    {
        
        s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
        s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
        return s;
    }

private:
    std::map<std::pair<unsigned int, unsigned int>, std::string> m_oData;

    unsigned int    m_nCols;
    unsigned int    m_nRows;
};


int main()
{
    CSV oCSV;

    oCSV.open( "test_in.csv" );
    oCSV( 0, 0 ) = "Column0";
    oCSV( 1, 1 ) = "100";
    oCSV( 2, 2 ) = "200";
    oCSV( 3, 3 ) = "300";
    oCSV( 4, 4 ) = "400";
    oCSV.save( "test_out.csv" );
    return 0;
}








COBOL:        IDENTIFICATION DIVISION.
       PROGRAM-ID. CSV.
       AUTHOR.  Bill Gunshannon.
       INSTALLATION.  Home.
       DATE-WRITTEN.  19 December 2021.
      ************************************************************
      ** Program Abstract:
      **   CSVs are something COBOL does pretty well.
      **     The commented out CONCATENATE statements are a
      **     second method other than the STRING method.
      ************************************************************
       
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
           REPOSITORY.
           FUNCTION ALL INTRINSIC.

       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
            SELECT CSV-File ASSIGN TO "csv.txt"
                 ORGANIZATION IS LINE SEQUENTIAL.
            SELECT Out-File ASSIGN TO "new.csv.txt"
                 ORGANIZATION IS LINE SEQUENTIAL.
       
       
       DATA DIVISION.
       
       FILE SECTION.
       
       FD  CSV-File 
           DATA RECORD IS CSV-Record.
       01  CSV-Record.
           05 Field1                  PIC X(64).
       
       FD  Out-File
           DATA RECORD IS Out-Line.
       01  Out-Line   PIC X(80).
       
       WORKING-STORAGE SECTION.
       
       01 Eof                   PIC X     VALUE 'F'.
       
       01  CSV-Data.
           05  CSV-Col1             PIC 9(5).
           05  CSV-Col2             PIC 9(5).
           05  CSV-Col3             PIC 9(5).
           05  CSV-Col4             PIC 9(5).
           05  CSV-Col5             PIC 9(5).

        01  CSV-Sum                  PIC ZZZ9.
        01  CSV-Sum-Alpha           
                REDEFINES     CSV-Sum  PIC X(4).
       
       PROCEDURE DIVISION.
       
       Main-Program.
           OPEN INPUT  CSV-File
           OPEN OUTPUT Out-File
           PERFORM Read-a-Record
           PERFORM Build-Header
           PERFORM UNTIL Eof = 'T'
              PERFORM Read-a-Record
              IF Eof NOT EQUAL 'T' PERFORM Process-a-Record
           END-PERFORM
           CLOSE CSV-File
           CLOSE Out-File
           STOP RUN.
       
       Read-a-Record.
           READ CSV-File 
              AT END MOVE 'T' TO Eof
           END-READ.

        Build-Header.
      **    MOVE CONCATENATE(TRIM(CSV-Record), ",SUM"
      **        TO Out-Line.
            STRING TRIM(CSV-Record), ",SUM" INTO Out-Line.
            WRITE Out-Line.
            MOVE SPACES TO Out-Line.

        Process-a-Record.
            UNSTRING CSV-Record DELIMITED BY ',' INTO
                  CSV-Col1 CSV-Col2 CSV-Col3 CSV-Col4 CSV-Col5.
            COMPUTE CSV-Sum = 
                  CSV-Col1 + CSV-Col2 + CSV-Col3 + CSV-Col4 + CSV-Col5.
      **    MOVE CONCATENATE(TRIM(CSV-Record), "," TRIM(CSV-Sum-Alpha))
      **        TO Out-Line.
            STRING TRIM(CSV-Record), "," TRIM(CSV-Sum-Alpha)
                   INTO Out-Line.
            WRITE Out-Line.
            MOVE SPACES TO Out-Line.
       
       END-PROGRAM.