// // This program reads a datafile in CSV format a la "gnuplot" // and generates a TGraph. // In first 3 simple cases ([y], [x,y], [x,y,ey] only the // datafile is needed if no column selection is required // // Calling sequence: // int table2graph(const char * datafile, // input file // const char * option = "", // x-y errors selection // const char * columns = "") // list of columns // // The datafile may have the following columns: // // y yvalues only, xvalues are auto provided: 0, 1, 2, .. // x, y simple graph, no error bars // x, y, ey symmetric errors y // x, y, ex, ey symmetric errors x, y, needs option "XY" // x, y, ey1, ey2 asymmetric errors y // x, y, ex symmetric errors x , needs option "X" // x, y, ex1, ex2 asymmetric errors x , needs option "X" // x, y, ex1, ex2, ey1, ey2 asymmetric errors x and y // // Separators may be spaces or commas. // lines starting with #, !, *, //, < are taken as comment and skipped // lines may have embedded text (non floats), text within quotes // like "line No 3" is completely ignored // // The options "X", "XY" are needed to resolve the ambiguity when // 3 or 4 columns are provided, // // The argument "columns" can be used to select columns in the datafile: // e.g. "0:4:5" : use colums 0, 4 and 5 for e.g. x, y, ey // The order of selected columns is arbitrary. // Default is to use all columns from the beginning. // If the file contains more columns than needed this argument // is compulsary // // The generated graph is written to a ROOT file opened in UPDATE mode // The ROOT filename is constructed from the datafile name with // its extension replaced by ".root" // // Author: Otto.Schaile@lmu.de //____________________________________________________________________ #include "TFile.h" #include "TGraph.h" #include "TGraphErrors.h" #include "TGraphAsymmErrors.h" #include "TObjArray.h" #include "TString.h" #include "TStyle.h" #include "TSystem.h" #include "TRegexp.h" #include #include namespace std {} using namespace std; //_____________________________________________________________________ Int_t ValueIsIn(Int_t *arr, Int_t maxv, Int_t val) { for (Int_t i = 0; i < maxv; i++) { if (val == arr[i]) return i; } return -1; } //_____________________________________________________________________ Int_t RemoveComment(TString *rl) { //remove spaces and inline comments TRegexp inquotes("\".*\""); // matches any quoted text incl " " TRegexp space(" +"); // matches any number of spaces TRegexp tab("\t+"); // matches any number of tabs Ssiz_t len, start = 0; Int_t nrem = 0; // remove text enclosed in quotes while ( 1 ) { start = inquotes.Index(*rl, &len); if (start < 0 ) break; rl->Replace(start, len, " "); nrem++; } while ( rl->Contains(" ") ) { (*rl)(space) = ","; nrem++; } while ( rl->Contains("\t") ) { (*rl)(tab) = ","; nrem++; } return nrem; } //_____________________________________________________________________ Int_t GetValues(const char *line, Double_t *val, Int_t maxv, Int_t * col_select, Int_t maxs) { // extract values from line at columns given by col_select // note: order of columns is arbitrary TString rline(line); TString sval; // cout << rline << endl; Bool_t ok; Int_t vcount = 0; // count columns in input Int_t ccount = 0; // count selected columns const Char_t * del = ","; Ssiz_t start = 0; while ( (ok = rline.Tokenize(sval ,start, del) ) ) { // use data field if it is a float, discard otherwise if ( sval.IsFloat() ) { if (maxs > 0) { Int_t ind = ValueIsIn(col_select, maxs, vcount); if (ind >= 0) { val[ind] = sval.Atof(); ccount++; } if (ccount >= maxs) { // cerr << "Too many values in: " << l << endl; break; } } else { val[ccount] = sval.Atof(); ccount++; } vcount ++; } } if ( maxs > 0 && ccount < maxs ) { cout << "Not enough values in: " << endl; cout << rline << endl; cout << " requested: " << maxs << endl; ccount = -1; } return ccount; } //____________________________________________________________________ Int_t table2graph(const char * datafile, const char * option = "", const char * columns = "") { enum {k_yonly, k_xy, k_xy_ey, k_xy_ex, k_xy_exey, k_xy_aey, k_xy_aex, k_xy_aex_aey}; TString sopt(option); sopt.ToUpper(); if (sopt.Length() > 0 && (sopt != "X" || sopt != "XY")) { cout << "Illegal option " << "\"" << sopt << "\"" << " must be \"X\" or \"XY\"" << endl; return -1; } TString cmd ("file "); cmd.Append(datafile); TString res = gSystem->GetFromPipe(cmd); if (res.Contains("with CRLF")) { cout << cmd << " " << res << endl; cout << "WARNING: Filetype is DOS" << endl; // return -2; } const Int_t maxcol = 6; TString rline; TGraph * graph = NULL; TGraph * gr = NULL; TGraphErrors * grerr = NULL; TGraphAsymmErrors * graerr = NULL; Int_t nval, ncolumns = 0, ncolumns_sel = 0,line_nr = -1, point_nr = 0; Int_t kind = -1; Int_t max_index = -1; Double_t values[maxcol]; Int_t col_select[maxcol]; TString scolumns(columns); // analyze requested column selection if any if (scolumns.Length() > 0 ) { for (Int_t i=0; i < maxcol; i++) { col_select[i] = 0; } const char * del = ":"; Int_t start = 0, vcount = 0; TString num; Bool_t ok; while ( (ok = scolumns.Tokenize(num ,start, del) ) ) { // cout << " " << num<< endl; if ( num.IsDec() ) { col_select[vcount] = num.Atoi(); vcount ++; if (vcount >= maxcol) { cerr << "Too many values in: " << columns << endl; return -4; } } } if ( vcount == 0 ) { cerr << "No valid numbers in: " << columns << endl; return -5; } else { ncolumns_sel = vcount; // cout << "ncolumns_sel: " << ncolumns_sel < max_index ) max_index = col_select[i]; cout << " " << col_select[i]; } cout << endl; } } else { for (Int_t i=0; i < maxcol; i++) { col_select[i] = i; } ncolumns_sel = 0; } // Open input file ifstream infile; infile.open(datafile, ios::in); if (!infile.good()) { cerr << "gnpl_datafile: " << gSystem->GetError() << " - " << datafile << endl; return -1; } // Loop on input file while ( 1 ) { rline.ReadLine(infile); if (infile.eof()) break; // check for DOS format , ends CRLF if ( rline.EndsWith("\r") ) { rline.Resize(rline.Length() - 1); } line_nr++; rline = rline.Strip(TString::kBoth); if (rline.BeginsWith("#") || rline.BeginsWith("*") || rline.BeginsWith("!") || rline.BeginsWith("//") || rline.BeginsWith("<") ) continue; RemoveComment(&rline); TObjArray *oa = rline.Tokenize(","); if (oa->GetEntries() < max_index+1) { cout << rline << endl; cout << "Not enough values, got: " << oa->GetEntries() << " needed "<< max_index+1 << endl; return -8; } Int_t nval = GetValues(rline.Data(), values, maxcol, col_select, ncolumns_sel); // cout << nval << " " << rline << endl; if ( nval < 0 ) break; if (nval == 0 ) continue; // the followin is only done for the first line // try to find which kind of Graph is needed if ( ncolumns == 0 ) { cout << "First line: \"" << rline << "\"" << endl; ncolumns = nval; if ( nval == 1 ) { cout << "case: only y values given" << endl; kind = k_yonly; } else if (nval == 2 ) { cout << "case:simple graph: x, y values" << endl; kind = k_xy; } else if (nval == 3 ) { if (sopt.Contains("X")) { // x, y, error x cout << "case: x, y values, err x" << endl; kind = k_xy_ex; } else { // x, y, error y cout << "case: x, y values, err y" << endl; kind = k_xy_ey; } } else if (nval == 4 ) { if (sopt.Contains("XY")) { // x, y, error x, y cout << "case: x, y values, err x and y" << endl; kind = k_xy_exey; } else if (sopt.Contains("X")) { // x, y, assym error x cout << "case: x, y values, assym err x" << endl; kind = k_xy_aex; } else { // x, y, assym error y cout << "case: x, y values, assym err y" << endl; kind = k_xy_aey; } } else if (nval == 6 ) { // x, y, assym error x, y cout << "case: x, y values, assym err x and assym y" << endl; kind = k_xy_aex_aey; } else { cerr << "At line: " << line_nr << " illegal number of values: " << nval << " should be <= 6 " << endl; return -2; } // construct graph according to number of columns provided if (kind == k_yonly || kind == k_xy) { gr = new TGraph(); graph = gr; } else if (kind == k_xy_ex || kind == k_xy_ey || kind == k_xy_exey) { grerr = new TGraphErrors(); graph = grerr; } else { graerr = new TGraphAsymmErrors(); graph = graerr; } } else { if ( nval != ncolumns ) { cerr << "At line: " << line_nr << " illegal number of values: " << nval << " should be " << ncolumns << endl; return -3; } } // now add point to the appropriate graph if (kind == k_yonly ) { gr->SetPoint(point_nr, (Double_t)point_nr, values[0]); } else if (kind == k_xy ) { gr->SetPoint(point_nr, values[0], values[1]); } else if (kind == k_xy_ex ) { grerr->SetPoint(point_nr, values[0], values[1]); grerr->SetPointError(point_nr, values[2], 0); } else if (kind == k_xy_ey ) { grerr->SetPoint(point_nr, values[0], values[1]); grerr->SetPointError(point_nr, 0, values[2]); } else if (kind == k_xy_exey ) { grerr->SetPoint(point_nr, values[0], values[1]); grerr->SetPointError(point_nr, values[2], values[3]); } else if (kind == k_xy_aex ) { graerr->SetPoint(point_nr, values[0], values[1]); graerr->SetPointError(point_nr, values[2], values[3], 0, 0); } else if (kind == k_xy_aey ) { graerr->SetPoint(point_nr, values[0], values[1]); graerr->SetPointError(point_nr, 0, 0, values[2], values[3]); } else if (kind == k_xy_aex_aey ) { graerr->SetPoint(point_nr, values[0], values[1]); graerr->SetPointError(point_nr, values[2], values[3], values[4], values[5]); } point_nr ++; } // display graph and write to ROOT file if (graph) { TString title(datafile); if (scolumns.Length() > 0 ) { title += " col"; if (scolumns.Contains(":")) title += "s"; title += ": "; title += columns; } graph->SetTitle(title); TString fname(datafile); Int_t sl; TRegexp suf("\\..*$"); fname(suf) = ""; graph->SetName(fname); graph->SetMarkerStyle(20); graph->SetMarkerSize(.8); graph->Draw("AP"); fname += ".root"; TFile * outfile = new TFile(fname, "UPDATE"); graph->Write(); outfile->Close(); cout << point_nr << " points added to " << title << endl; cout << "Graph written to " << fname << endl; } return point_nr; }