12.4. Reading ROOT Files
Table of Contents
Opening files
To use data or objects saved in a .root file, you first need to open that file with the TFile class. Conceptually, TFile behaves like a C++ stream combined with a directory of objects: it gives you both access to the file itself and a way to look up what is inside.
The most common way to open a ROOT file is through its constructor. In the ROOT prompt you can write
TFile *f = new TFile("results.root");
ROOT will try to open results.root in read mode. If the file does not exist, the pointer will still be created, but the file will be marked as a zombie, which means it is invalid for normal use. You should always check that the file was opened successfully before using it.
A simple safety pattern is
TFile *f = TFile::Open("results.root", "READ");
if (!f || f->IsZombie()) {
std::cout << "Error opening file results.root" << std::endl;
return;
}
Here TFile::Open is a convenient static method that returns a pointer, and the second argument "READ" selects read only mode. Other modes, such as "RECREATE" or "UPDATE", are covered elsewhere in the ROOT Files chapter, so here you only need to remember that when you are reading files, "READ" is the appropriate choice.
You can also open ROOT files directly from the command line when starting ROOT. For example
root results.root
will start ROOT and automatically open results.root as the current file. In the interactive session, the file is accessible through the global pointer gFile. You can inspect which file is currently associated with gFile by printing
gFile->GetName();or by calling
gFile->ls();to list its contents.
When you have finished reading from a file, close it explicitly, especially in macros and longer analyses.
f->Close();
delete f;Closing ensures that any internal buffers are flushed and that the file is properly detached from memory.
Always check if (!f || f->IsZombie()) after opening a ROOT file for reading. Using an invalid TFile pointer is a frequent source of crashes and confusing errors.
If you expect to open files inside functions or loops, it is good practice to keep the pointer scope as small as possible. Open the file, retrieve what you need, then close and delete the TFile before leaving the scope, so you do not accumulate open files or rely on global state.
Retrieving objects
Once a ROOT file is open, the next step is to access the objects that were written to it, such as histograms, graphs, TTrees, or directories. In most simple cases, the objects are stored at the top level of the file and can be accessed by their names. When objects were written, each had an internal key that matches its object name. To retrieve an object, you use TFile::Get.
Suppose a one dimensional histogram named "hEnergy" was saved to the file. You can read it back with
TH1F *hEnergy = (TH1F*) f->Get("hEnergy");
The Get method returns a generic TObject*, so you must cast it to the correct class. This cast is your responsibility, because ROOT cannot know at compile time which concrete type is stored under a given name. If you cast to the wrong type, you may see undefined behavior. You can protect yourself by checking the returned pointer:
TH1F *hEnergy = (TH1F*) f->Get("hEnergy");
if (!hEnergy) {
std::cout << "Histogram hEnergy not found in file" << std::endl;
}
For trees, the procedure is similar. If a TTree was saved with the name "Events", retrieve it with
TTree *tree = (TTree*) f->Get("Events");
if (!tree) {
std::cout << "TTree Events not found" << std::endl;
}
After that, you can use all the usual TTree methods, such as Print, Draw, or manual loops.
Often, you are not entirely sure which objects are present, or what their exact names are. Before retrieving, list the file contents with
f->ls();This command prints a hierarchical view of objects and directories inside the file, together with their class types. For example, you might see lines like
TH1F hEnergy;1 Energy spectrum
TTree Events;1 Event data
The left entry is the class, the first word after that is the key name, which you pass to Get.
If objects are stored inside subdirectories in the file, you must include the directory path in the name. For example, if hEnergy is in a directory named "spectra", you retrieve it with
TH1F *hEnergy = (TH1F*) f->Get("spectra/hEnergy");Alternatively, you can navigate into the directory first:
f->cd("spectra");
TH1F *hEnergy = (TH1F*) gDirectory->Get("hEnergy");
Here gDirectory represents the current directory inside the file. If you use this style, you need to pay attention to which directory is active, especially in larger macros.
In interactive work, you can use the ROOT browser to explore files and pick objects. Start the browser with
TBrowser b;
then click on the file, directories, and objects. Double clicking a histogram or a graph will draw it automatically. The browser shows you the object names, which you can then reuse in your code when calling Get.
Sometimes you only know the class but not the exact object names. In that case, you can iterate over the list of keys. A simple pattern is
TIter nextkey(f->GetListOfKeys());
TKey *key;
while ((key = (TKey*) nextkey())) {
std::cout << key->GetName() << " " << key->GetClassName() << std::endl;
}This technique is useful for files that contain many similar objects, for example a histogram per detector channel.
ROOT tracks object ownership, so it is important to understand who owns objects you retrieve. In most common use cases, when you call Get, the object is owned by the file or by the current directory. That means you should not delete the retrieved object yourself if you intend to keep the file open. If you close the file while still using the object, you might access invalid memory. A safe pattern is to finish using the objects, then close the file at the end of your analysis.
Make sure the type you cast from Get matches the class that was written. Always check that the pointer returned by Get is not null before using it, and keep the TFile open as long as you are using objects that the file owns.
If you need to keep a copy of an object after closing the file, you can clone it:
TH1F *hEnergy = (TH1F*) f->Get("hEnergy");
TH1F *hCopy = (TH1F*) hEnergy->Clone("hEnergy_copy");
f->Close(); // hCopy is still valid, because it is now independent of the fileThe cloned object is stored in memory and is no longer tied to the file's lifetime. This is a common pattern when you want to process or modify histograms from an input file while keeping your analysis code independent from the original storage.
Views: 13
KAHIBARO