meteo-vm2  2.0.11
parser.cc
1 /*
2  * parser - Parse VM2 files
3  *
4  * Copyright (C) 2012,2013 Arpae-SIMC <simc-urp@arpae.it>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Author: Emanuele Di Giacomo <edigiacomo@arpae.it>
21  */
22 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
23 
24 #include <meteo-vm2/parser.h>
25 
26 #include <cstdlib>
27 #include <iomanip>
28 #if GCC_VERSION >= 40900
29 #include <regex>
30 #else
31 #include <regex.h>
32 #endif
33 
34 
35 namespace meteo {
36 namespace vm2 {
37 
38 
39 ParserException::ParserException(int lineno, const std::string& msg) : std::runtime_error("line " + std::to_string(lineno) + ": " + msg) {}
40 
41 std::string Parser::regexp_str = "^([0-9]{12}([0-9][0-9])?),([0-9]+),([0-9]+),([+-]?[0-9.]*),([+-]?[0-9.]*),([^,\n\r]*),([^,\n\r]*[\r\n]*)$";
42 
43 #if GCC_VERSION >= 40900
44 static std::regex regexp(Parser::regexp_str);
45 #else
46 struct Regexp {
47  regex_t reg;
48 
49  Regexp(const std::string& re) {
50  if (int i = regcomp(&reg, re.c_str(), REG_EXTENDED) != 0) {
51  char b[1024];
52  regerror(i, &reg, b, sizeof(b));
53  std::runtime_error("Regex error: " + std::string(b));
54  }
55  }
56  ~Regexp() {
57  regfree(&reg);
58  }
59 };
60 
61 struct RegexpMatch {
62  int nmatch;
63  regmatch_t* regmatch;
64  std::string val;
65 
66  RegexpMatch(int nmatch) : nmatch(nmatch), regmatch(new regmatch_t[nmatch]) {}
67  ~RegexpMatch() {
68  delete[] regmatch;
69  }
70  std::string str(int i) {
71  if (i >= nmatch)
72  throw std::out_of_range("out of range: " + std::to_string(i));
73  return val.substr(regmatch[i].rm_so, regmatch[i].rm_eo - regmatch[i].rm_so);
74  }
75 };
76 
77 bool regex_match(const std::string& s, RegexpMatch& match, const Regexp& re) {
78  int r = regexec(&re.reg, s.c_str(), match.nmatch, match.regmatch, 0);
79  if (r != 0)
80  return false;
81  match.val = s;
82  return true;
83 }
84 
85 static Regexp regexp(Parser::regexp_str);
86 #endif
87 
88 Parser::Parser(std::istream& in) : in(in), lineno(0) {}
89 Parser::~Parser() {}
90 
91 bool Parser::next(Value& value) {
92  std::string line;
93  return next(value, line);
94 }
95 
96 bool Parser::next(Value& value, std::string& line) {
97  char c;
98 
99  line.clear();
100  while (true) {
101  in.get(c);
102  if (!in.good()) {
103  if (line.size() == 0) {
104  return false;
105  } else {
106  break;
107  }
108  }
109  if (c == '\r') {
110  continue;
111  }
112  if (c == '\n') {
113  break;
114  }
115  line.append(1, c);
116  }
117  ++lineno;
118 
119 #if GCC_VERSION >= 40900
120  std::smatch match;
121 #else
122  RegexpMatch match(9);
123 #endif
124  if (not regex_match(line, match, regexp))
125  throw ParserException(lineno, "pattern mismatch");
126 
127  // match[0]: line
128  // match[1]: datetime
129  // match[2]: seconds (optional)
130  // match[3]: station id
131  // match[4]: variable id
132  // match[5]: value1
133  // match[6]: value2
134  // match[7]: value3
135  // match[8]: flags
136 
137  value.sec = 0;
138  sscanf(match.str(1).c_str(), "%04d%02d%02d%02d%02d%02d",
139  &value.year, &value.month, &value.mday,
140  &value.hour, &value.min, &value.sec);
141  value.station_id = strtoul(match.str(3).c_str(), NULL, 10);
142  value.variable_id = strtoul(match.str(4).c_str(), NULL, 10);
143  value.value1 = (match.str(5).empty() ? vm2::MISSING_DOUBLE : strtod(match.str(5).c_str(), NULL));
144  value.value2 = (match.str(6).empty() ? vm2::MISSING_DOUBLE : strtod(match.str(6).c_str(), NULL));
145  value.value3 = match.str(7);
146  value.flags = match.str(8);
147 
148  return true;
149 }
150 
151 void Parser::serialize(std::ostream& out, const Value& value) {
152  out << std::setfill('0') << std::setw(4) << value.year
153  << std::setfill('0') << std::setw(2) << value.month
154  << std::setfill('0') << std::setw(2) << value.mday
155  << std::setfill('0') << std::setw(2) << value.hour
156  << std::setfill('0') << std::setw(2) << value.min
157  << std::setfill('0') << std::setw(2) << value.sec
158  << ","
159  << value.station_id
160  << ","
161  << value.variable_id
162  << ","
163  << (value.value1 != vm2::MISSING_DOUBLE ? std::to_string(value.value1) : "")
164  << ","
165  << (value.value2 != vm2::MISSING_DOUBLE ? std::to_string(value.value2) : "")
166  << ","
167  << value.value3 << ","
168  << value.flags << std::endl;
169 }
170 
171 }
172 }
Parse VM2 files.
int lineno
Number of the last line parsed.
Definition: parser.h:60
std::istream & in
Input stream.
Definition: parser.h:58
static void serialize(std::ostream &out, const Value &value)
Serialize a value.
Definition: parser.cc:151
bool next(Value &value)
Read the next VM2 value.
Definition: parser.cc:91