Cod sursa(job #1358596)

Utilizator RazzinnatorRazvan Brinzea Razzinnator Data 24 februarie 2015 18:15:51
Problema Convertor Scor 100
Compilator cpp Status done
Runda rosedu_cdl_2015 Marime 6.73 kb
#include <fstream>
#include <string>
#include <vector>

#include <string.h>

using namespace std;

// Citeste un fisier intreg si stocheaza
// tot continutul sau intr-un string,
// pe care il returneaza ulterior.
string read_data( string filename )
{
    ifstream f( filename.c_str() );
    string s, data;

    while( getline( f, s ) )
    {
        data.append( s );
    }

    f.close();
    return data;
}

// Primeste un string
// care contine date de tip JSON ( in formatul
// specificat in enunt ), si extrage
// un vector de string-uri care contine
// denumirile campurilor din obiectele JSON.
//
// Se parcurge doar primul obiect pentru determinarea
// denumirilor campurilor.
// Se returneaza vectorul extras.
vector<string> extract_keys( string data )
{
    bool started_field = false;
    vector<string> keys;
    string key;
    size_t starting_pos;

    // Campurile sunt intotdeauna siruri de caractere
    // incadrate in ghilimele.
    // Astfel, se cauta mereu fie ghilimele, care marcheaza
    // inceputul sau sfarsitul unui camp, fie caracterul },
    // care marcheaza sfarsitul primului obiect, si finalizarea
    // rularii functiei.
    size_t found = data.find_first_of( "\"}");
    while( data[found] != '}' )
    {
        // Daca s-au gasit ghilimele, inca nu s-a terminat
        // executia structurii 'while'.
        // De fiecare data cand se vor gasi ghilimele,
        // acestea vor marca fie inceputul, fie sfarsitul unui camp.
        //
        // Daca ele se gasesc la inceputul campului, se retine pozitia de inceput
        // si se cauta in continuare urmatoarele ghilimele.
        //
        // Daca ele se gasesc la sfarsitul campului, se extrage intr-un string separat.
        // care se introduce intr-un vector, subsirul care reprezinta denumirea campului.
        //
        // Procedeul se repeta pana la sfarsitul primului obiect (pentru ca si urmatoarele
        // au tot aceleasi campuri).
        if( started_field == false )
        {
            started_field = true;
            starting_pos = found;
        }
        else
        {
            started_field = false;
            key.assign( data, starting_pos + 1, found - starting_pos - 1 );
            keys.push_back( key );
            found = data.find_first_of( ",}", found );
            if( data[found] == '}' )
            {
                break;
            }
        }
        found = data.find_first_of( "\"}", found+1 );
    }

    return keys;
}


// Primeste un string
// care contine date de tip JSON ( in formatul
// specificat in enunt ) si un indice de pozitie
// care marcheaza inceputul unui obiect JSON.
// Functia extrage valorile din fiecare camp al
// obiectului, le stocheaza intr-un vector si le returneaza.
// (fara a mai retine si denumirile campurilor)
vector<string> extract_values( string data, size_t position )
{
    vector<string> values;
    string value;

    // Procedeul pentru extragerea valorilor va fi similar cu cel pentru
    // extragerea campurilor, doar ca valorile pot fi si in forma numerica,
    // nu numai in forma unui sir de caractere.
    //
    // Extragerea se incepe de la pozitia trimisa ca parametru
    // si continua pana la intalnirea caracterului '}', care marcheaza
    // sfarsitul obiectului.
    //
    // Intotdeauna, valoarea unui camp se gaseste dupa caracterul ':',
    // astfel ca de fiecare data se cauta fie acest caracter,
    // fie caracterul '}'.
    // Cand este gasit caracterul ':', se avanseaza manual
    // indicele current_pos pentru a sari peste
    // whitespace-ul care s-ar putea gasi intre caracter si valoarea efectiva.
    //
    // Daca dupa whitespace se intalneste caracterul '"', inseamna ca incepe
    // o valoare care este sub forma unui sir de caractere. Se cauta urmatorul
    // caracter '"', care marcheaza sfarsitul valorii, si se extrage continutul.
    //
    // Daca dupa whitespace nu se intalneste caracterul '"', sigur se va intalni o cifra,
    // si va fi o valoare numerica.
    // In acest caz, se va avansa manual pana la finalul valorii numerice, iar valoarea
    // va fi extrasa (dar tot sub forma unui string).
    size_t starting_pos = data.find_first_of( ":}", position+1 );
    size_t current_pos = starting_pos;
    while( data[current_pos] != '}' )
    {
        while( strchr( "\"0123456789", data[current_pos] ) == 0 )
        {
            current_pos++;
        }

        starting_pos = current_pos;

        if( data[current_pos] == '"' )
        {
            current_pos = data.find( '"' , current_pos+1 );
            value.assign( data, starting_pos+1, current_pos - starting_pos - 1 );
            values.push_back( value );
        }
        else
        {
            while( data[current_pos] >= '0' && data[current_pos] <= '9' )
            {
                current_pos++;
            }
            value.assign( data, starting_pos, current_pos - starting_pos );
            values.push_back( value );
        }
        current_pos = data.find_first_of( ":}", current_pos );
    }

    return values;
}

// Primeste un vector de string-uri
// si il scrie intr-un fisier, in continuarea
// datelor deja existente, in format CSV.
void print_csv_vector( vector<string> line, string filename )
{
    // Se deschide fisierul dat ca parametru,
    // folosindu-se flagurile ios::out si ios::app,
    // pentru ca scrierea sa se faca in continuarea
    // datelor deja existente.
    ofstream g;
    g.open( filename.c_str(), ios::out | ios::app );

    for( int i = 0; i < line.size(); i++ )
    {
        g << line[i] << ',';
    }
    g << endl;

    g.close();
}

int main()
{
    string data;
    vector<string> keys;
    vector<string> values;

    // Se goleste fortat continutul fisierului convertor.out
    ofstream g( "convertor.out" );
    g.close();

    // Se foloseste functia read_data pentru a citi intreg fisierul
    data = read_data( "convertor.in" );

    // Se extrag doar denumirile campurilor obiectelor,
    // obtinandu-se un vector de string-uri care este
    // scris ca antet al fisierului output.
    keys = extract_keys( data );
    print_csv_vector( keys, "convertor.out" );

    // Pentru fiecare obiect, se extrag valorile campurilor,
    // fara a se mai retine si denumirile acestora.
    // Se afiseaza valorile pe cate o linie noua, si se trece
    // la urmatorul obiect dupa fiecare pas, pana cand
    // se termina toate obiectele.
    size_t position = data.find( '{' );
    while( position != string::npos )
    {
        values = extract_values( data, position );
        print_csv_vector( values, "convertor.out" );
        position = data.find( '{', position+1 );
    }

    return 0;
}