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