ROOT  6.06/09
Reference Guide
TreeUtils.cxx
Go to the documentation of this file.
1 // @(#)root/tree:$Id$
2 // Author: Timur Pocheptsov 30/01/2014
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2014, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class TreeUtils
13 Different standalone functions to work with trees and tuples,
14 not reqiuired to be a member of any class.
15 */
16 
17 #include <istream>
18 #include <cassert>
19 #include <cctype>
20 
21 #include "TreeUtils.h"
22 #include "TNtupleD.h"
23 #include "TNtuple.h"
24 #include "TError.h"
25 #include "TTree.h"
26 
27 namespace ROOT {
28 namespace TreeUtils {
29 
30 //Some aux. functions to read tuple from a text file. No reason to make them memeber-functions.
31 void SkipEmptyLines(std::istream &input);
32 void SkipWSCharacters(std::istream &input);
33 bool NextCharacterIsEOL(std::istream &input);
34 
35 //Enforce/limit what can be a Tuple.
36 //Actually, at the moment you can only use
37 //the fill function for TNtuple/TNtupleD
38 //(enforced by hidden definition and explicit instantiations).
39 //But in future this can potentially change.
40 
41 //TODO: there is no line number in any of error messages.
42 //It can be improved, though, we can have mixed line endings
43 //so I can not rely on this numbering (for example, my vim shows these lines:
44 //aaaa\r\r\nbbb as
45 //aaaa
46 //bbbb
47 //Though it can be also treated as
48 //aaaa
49 //
50 //bbb
51 //or even as
52 //aaaa
53 //
54 //
55 //bbb - so line numbers can be useless and misleading.
56 
57 template<class> struct InvalidTupleType;
58 
59 template<>
60 struct InvalidTupleType<TNtuple>
61 {
62  InvalidTupleType()
63  {
64  }
65 };
66 
67 template<>
68 struct InvalidTupleType<TNtupleD>
69 {
70  InvalidTupleType()
71  {
72  }
73 };
74 
75 ////////////////////////////////////////////////////////////////////////////////
76 
77 template<class DataType, class Tuple>
78 Long64_t FillNtupleFromStream(std::istream &inputStream, Tuple &tuple, char delimiter, bool strictMode)
79 {
80  InvalidTupleType<Tuple> typeChecker;
81 
82  if (delimiter == '\r' || delimiter == '\n') {
83  ::Error("FillNtupleFromStream", "invalid delimiter - newline character");
84  return 0;
85  }
86 
87  if (delimiter == '#') {
88  ::Error("FillNtuplesFromStream", "invalid delimiter, '#' symbols can only start a comment");
89  return 0;
90  }
91 
92  const Int_t nVars = tuple.GetNvar();
93  if (nVars <= 0) {
94  ::Error("FillNtupleFromStream", "invalid number of elements");
95  return 0;
96  }
97 
98  DataType *args = tuple.GetArgs();
99  assert(args != 0 && "FillNtupleFromStream, args buffer is a null");
100 
101  Long64_t nLines = 0;
102 
103  if (strictMode) {
104  while (true) {
105  //Skip empty-lines (containing only newlines, comments, whitespaces + newlines
106  //and combinations).
107  SkipEmptyLines(inputStream);
108 
109  if (!inputStream.good()) {
110  if (!nLines)
111  ::Error("FillNtupleFromStream", "no data read");
112  return nLines;
113  }
114 
115  //Now, we have to be able to read _the_ _required_ number of entires.
116  for (Int_t i = 0; i < nVars; ++i) {
117  SkipWSCharacters(inputStream);//skip all wses except newlines.
118  if (!inputStream.good()) {
119  ::Error("FillNtupleFromStream", "failed to read a tuple (not enough values found)");
120  return nLines;
121  }
122 
123  if (i > 0 && !std::isspace(delimiter)) {
124  const char test = inputStream.peek();
125  if (!inputStream.good() || test != delimiter) {
126  ::Error("FillNtupleFromStream", "delimiter expected");
127  return nLines;
128  }
129 
130  inputStream.get();//we skip a dilimiter whatever it is.
131  SkipWSCharacters(inputStream);
132  }
133 
134  if (NextCharacterIsEOL(inputStream)) {
135  //This is unexpected!
136  ::Error("FillNtupleFromStream", "unexpected character or eof found");
137  return nLines;
138  }
139 
140  inputStream>>args[i];
141 
142  if (!(inputStream.eof() && i + 1 == nVars) && !inputStream.good()){
143  ::Error("FillNtupleFromStream", "error while reading a value");
144  return nLines;
145  }
146  }
147 
148  SkipWSCharacters(inputStream);
149  if (!NextCharacterIsEOL(inputStream)) {
150  ::Error("FillNtupleFromStream",
151  "only whitespace and new line can follow the last number on the line");
152  return nLines;
153  }
154 
155  //Only God forgives :) Ugly but ...
156  //for TNtuple it's protected :)
157  static_cast<TTree &>(tuple).Fill();
158  ++nLines;
159  }
160  } else {
161  Int_t i = 0;//how many values we found for a given tuple's entry.
162  while (true) {
163  //Skip empty lines, comments and whitespaces before
164  //the first 'non-ws' symbol:
165  //it can be a delimiter/a number (depends on a context) or an invalid symbol.
166  SkipEmptyLines(inputStream);
167 
168  if (!inputStream.good()) {
169  //No data to read, check what we read by this moment:
170  if (!nLines)
171  ::Error("FillNtupleFromStream", "no data read");
172  else if (i > 0)//we've read only a part of a row.
173  ::Error("FillNtupleFromStream", "unexpected character or eof found");
174  return nLines;
175  }
176 
177  if (i > 0 && !std::isspace(delimiter)) {
178  //The next one must be a delimiter.
179  const char test = inputStream.peek();
180  if (!inputStream.good() || test != delimiter) {
181  ::Error("FillNtupleFromStream", "delimiter expected (non-strict mode)");
182  return nLines;
183  }
184 
185  inputStream.get();//skip the delimiter.
186  SkipEmptyLines(inputStream);//probably, read till eof.
187  }
188 
189  //Here must be a number.
190  inputStream>>args[i];
191 
192  if (!(inputStream.eof() && i + 1 == nVars) && !inputStream.good()){
193  ::Error("FillNtupleFromStream", "error while reading a value");
194  return nLines;
195  }
196 
197  if (i + 1 == nVars) {
198  //We god the row, can fill now and continue.
199  static_cast<TTree &>(tuple).Fill();
200  ++nLines;
201  i = 0;
202  } else
203  ++i;
204  }
205  }
206 
207  return nLines;
208 }
209 
210 template Long64_t FillNtupleFromStream<Float_t, TNtuple>(std::istream &, TNtuple &, char, bool);
211 template Long64_t FillNtupleFromStream<Double_t, TNtupleD>(std::istream &, TNtupleD &, char, bool);
212 
213 //Aux. functions to read tuples from text files.
214 
215 //file:
216 // lines
217 //
218 //lines:
219 // line
220 // line lines
221 //
222 //line:
223 // comment
224 // tuple
225 // empty-line
226 
227 //comment:
228 // '#' non-newline-character-sequence newline-character
229 //
230 //non-newline-character-sequence:
231 // any symbol except '\r' or '\n'
232 //
233 //newline-character:
234 // '\r' | '\n'
235 ////////////////////////////////////////////////////////////////////////////////
236 /// Skips everything from '#' to (including) '\\r' or '\\n'.
237 
238 void SkipComment(std::istream &input)
239 {
240  while (input.good()) {
241  const char next = input.peek();
242  if (input.good()) {
243  input.get();
244  if (next == '\r' || next == '\n')
245  break;
246  }
247  }
248 }
249 
250 //empty-line:
251 // newline-character
252 // ws-sequence newline-character
253 // ws-sequence comment
254 ////////////////////////////////////////////////////////////////////////////////
255 /// Skips empty lines (newline-characters), ws-lines (consisting only of whitespace characters + newline-characters).
256 
257 void SkipEmptyLines(std::istream &input)
258 {
259  while (input.good()) {
260  const char c = input.peek();
261  if (!input.good())
262  break;
263 
264  if (c == '#')
265  SkipComment(input);
266  else if (!std::isspace(c))//'\r' and '\n' are also 'isspaces'.
267  break;
268  else
269  input.get();
270  }
271 }
272 
273 //ws-sequence:
274 // c such that isspace(c) and c is not a newline-character.
275 ////////////////////////////////////////////////////////////////////////////////
276 /// Skip whitespace characters, but not newline-characters we support ('\\r' or '\\n').
277 
278 void SkipWSCharacters(std::istream &input)
279 {
280  while (input.good()) {
281  const char next = input.peek();
282  if (input.good()) {
283  if (std::isspace(next) && next != '\n' && next != '\r')
284  input.get();
285  else
286  break;
287  }
288  }
289 }
290 
291 //Next character is either newline-character, eof or we have some problems reading
292 //the next symbol.
293 ////////////////////////////////////////////////////////////////////////////////
294 /// Either '\\r' | '\\n' or eof of some problem.
295 
296 bool NextCharacterIsEOL(std::istream &input)
297 {
298  if (!input.good())
299  return true;
300 
301  const char next = input.peek();
302  if (!input.good())
303  return true;
304 
305  return next == '\r' || next == '\n';
306 }
307 
308 }//TreeUtils
309 }//ROOT
long long Long64_t
Definition: RtypesCore.h:69
Namespace for new ROOT classes and functions.
Definition: ROOT.py:1
A simple TTree restricted to a list of double variables only.
Definition: TNtupleD.h:30
ClassImp(TSeqCollection) Int_t TSeqCollection TIter next(this)
Return index of object in collection.
#define assert(cond)
Definition: unittest.h:542
template Long64_t FillNtupleFromStream< Float_t, TNtuple >(std::istream &, TNtuple &, char, bool)
int Int_t
Definition: RtypesCore.h:41
void SkipEmptyLines(std::istream &input)
Skips empty lines (newline-characters), ws-lines (consisting only of whitespace characters + newline-...
Definition: TreeUtils.cxx:257
A simple TTree restricted to a list of float variables only.
Definition: TNtuple.h:30
Long64_t FillNtupleFromStream(std::istream &inputStream, Tuple &tuple, char delimiter, bool strictMode)
Definition: TreeUtils.cxx:78
bool NextCharacterIsEOL(std::istream &input)
Either '\r' | '\n' or eof of some problem.
Definition: TreeUtils.cxx:296
void SkipComment(std::istream &input)
Skips everything from '#' to (including) '\r' or '\n'.
Definition: TreeUtils.cxx:238
template Long64_t FillNtupleFromStream< Double_t, TNtupleD >(std::istream &, TNtupleD &, char, bool)
A TTree object has a header with a name and a title.
Definition: TTree.h:94
void SkipWSCharacters(std::istream &input)
Skip whitespace characters, but not newline-characters we support ('\r' or '\n'). ...
Definition: TreeUtils.cxx:278
void Error(ErrorHandler_t func, int code, const char *va_(fmt),...)
Write error message and call a handler, if required.