KAHIBARO
Discord Login Register

15.1. Importing Text Data

Reading TXT files

Text files are a common first format for real experimental data. In ROOT you usually read them with standard C++ I/O, then convert the values into ROOT objects such as histograms or TTrees. The goal in this chapter is to focus on the mechanics of getting plain text into your ROOT program, not on how you later store or analyze it.

Most plain text data files are organized as rows and columns. Each row corresponds to one event or measurement, and columns are separated by spaces or tabs. Comment lines often begin with a character such as # or //. A simple example might look like:

text
# run event energy time
1  1  3.41  12.5
1  2  2.98  12.7
1  3  3.05  12.4

The basic steps to read such a file inside a ROOT macro are always the same. You open the file with an input stream, loop over lines or values, extract numbers with the stream operator, and skip comments or malformed rows.

A typical pattern in a ROOT macro is:

cpp
void read_txt_example() {
   std::ifstream infile("data.txt");
   if (!infile.is_open()) {
      std::cerr << "Error opening file data.txt\n";
      return;
   }
   int run, event;
   double energy, time;
   // Optionally skip a header line that starts with '#'
   std::string line;
   while (std::getline(infile, line)) {
      if (line.size() == 0) continue;
      if (line[0] == '#') continue;
      std::istringstream iss(line);
      if (!(iss >> run >> event >> energy >> time)) {
         // Could not parse this line, skip it
         continue;
      }
      // Here you would fill histograms or a TTree
      // hEnergy->Fill(energy);
   }
}

This style, reading whole lines and then parsing them, gives you good control over comments, missing values, and mixed content. For very simple files where every line is purely numeric and there is no header, you can read directly from the stream:

cpp
while (infile >> run >> event >> energy >> time) {
   // Use the values
}

However, if there are comment lines, this approach will fail when it reaches a nonnumeric token. For real experimental files, it is usually safer to read line by line and check for comment markers and empty lines yourself.

You should also decide in advance what to do when a line does not contain the expected number of values. For example, you may want to count how many lines were skipped, or print a warning only for the first few problematic lines. ROOT itself does not impose any particular policy here, so you design the logic that fits your dataset.

If your TXT file uses a different delimiter, for example semicolons or multiple spaces, you can still handle it with a combination of std::getline, manual splitting on delimiters, or std::istringstream. The core idea remains to read textual rows and convert them into numeric C++ variables.

Always validate each line before using the numeric values in your analysis, and handle comment lines, empty lines, and malformed rows explicitly.

Reading CSV files

CSV files are a special type of text file where columns are separated by a specific delimiter, typically a comma. Experimental data exported from spreadsheets or lab software is often in CSV format. The first row is frequently a header that contains the column names.

A simple CSV could look like:

text
run,event,energy,time
1,1,3.41,12.5
1,2,2.98,12.7
1,3,3.05,12.4

From ROOT you still use standard C++ I/O to read CSV files, but you must split each line at commas instead of relying on whitespace. The general workflow is similar to the TXT case: open the file, optionally skip or parse a header, then loop over data rows and convert each field into numbers.

One common pattern is to use std::getline twice. First you read each whole line from the file, then you use another std::getline on a std::stringstream to separate the fields at commas:

cpp
void read_csv_example() {
   std::ifstream infile("data.csv");
   if (!infile.is_open()) {
      std::cerr << "Error opening file data.csv\n";
      return;
   }
   std::string line;
   // Read header line and ignore it, or parse column names if needed
   if (!std::getline(infile, line)) {
      std::cerr << "Empty CSV file\n";
      return;
   }
   while (std::getline(infile, line)) {
      if (line.size() == 0) continue;
      std::stringstream ss(line);
      std::string field;
      std::string sRun, sEvent, sEnergy, sTime;
      // Extract four comma separated fields
      if (!std::getline(ss, sRun,   ',')) continue;
      if (!std::getline(ss, sEvent, ',')) continue;
      if (!std::getline(ss, sEnergy,',')) continue;
      if (!std::getline(ss, sTime,  ',')) continue;
      int run    = std::stoi(sRun);
      int event  = std::stoi(sEvent);
      double energy = std::stod(sEnergy);
      double time   = std::stod(sTime);
      // Now you can fill histograms or TTrees
      // hEnergy->Fill(energy);
   }
}

Here, each field is first kept as a string and then converted using std::stoi or std::stod. This approach makes it easier to check for missing values or special markers like "NA" or "null" before conversion. For instance, you could skip lines where a field is empty, or assign default values.

CSV files that come from spreadsheets can have extra spaces around commas, or use semicolons as separators depending on the locale. To handle such cases you can trim whitespace from the fields or explicitly choose another delimiter in the inner getline call.

Sometimes a CSV header is important, because it contains the semantic meaning of each column. In that case you can parse the first line, split it at commas, and store the column names in a vector of strings. This can be useful for automatically mapping columns to variables without hard coding the positions.

While ROOT does not provide a dedicated, high level CSV importer in its core, the combination of standard C++ I/O and string handling is flexible enough for most experimental data files. Once you have numbers in C++ variables, you can create TTrees or histograms that match the structure of your CSV file.

When reading CSV files, always treat the first line as a potential header, handle the chosen delimiter consistently, and check each field for emptiness or nonnumeric content before converting it to a number.

Views: 10

Comments

Please login to add a comment.

Don't have an account? Register now!