1 /**
2 * @file stringUtils.cpp
3 * Contains definitions for string manipulation functions
4 * within Cantera.
5 */
6
7 // This file is part of Cantera. See License.txt in the top-level directory or
8 // at https://cantera.org/license.txt for license and copyright information.
9
10 //@{
11 #include "cantera/base/ct_defs.h"
12
13 #ifdef _MSC_VER
14 #define SNPRINTF _snprintf
15 #else
16 #define SNPRINTF snprintf
17 #endif
18 //@}
19
20 #include "cantera/base/stringUtils.h"
21 #include "cantera/base/ctexceptions.h"
22 #include "cantera/base/utilities.h"
23 #include "cantera/base/global.h"
24
25 #include <boost/algorithm/string.hpp>
26 #include <sstream>
27
28 namespace ba = boost::algorithm;
29
30 namespace Cantera
31 {
32
vec2str(const vector_fp & v,const std::string & fmt,const std::string & sep)33 std::string vec2str(const vector_fp& v, const std::string& fmt,
34 const std::string& sep)
35 {
36 char buf[64];
37 std::stringstream o;
38 for (size_t i = 0; i < v.size(); i++) {
39 SNPRINTF(buf, 63, fmt.c_str(), v[i]);
40 o << buf;
41 if (i != v.size() - 1) {
42 o << sep;
43 }
44 }
45 return o.str();
46 }
47
stripnonprint(const std::string & s)48 std::string stripnonprint(const std::string& s)
49 {
50 std::string ss = "";
51 for (size_t i = 0; i < s.size(); i++) {
52 if (isprint(s[i])) {
53 ss += s[i];
54 }
55 }
56 return ss;
57 }
58
parseCompString(const std::string & ss,const std::vector<std::string> & names)59 compositionMap parseCompString(const std::string& ss,
60 const std::vector<std::string>& names)
61 {
62 compositionMap x;
63 for (size_t k = 0; k < names.size(); k++) {
64 x[names[k]] = 0.0;
65 }
66
67 size_t start = 0;
68 size_t stop = 0;
69 size_t left = 0;
70 while (stop < ss.size()) {
71 size_t colon = ss.find(':', left);
72 if (colon == npos) {
73 break;
74 }
75 size_t valstart = ss.find_first_not_of(" \t\n", colon+1);
76 stop = ss.find_first_of(", ;\n\t", valstart);
77 std::string name = ba::trim_copy(ss.substr(start, colon-start));
78 if (!names.empty() && x.find(name) == x.end()) {
79 throw CanteraError("parseCompString",
80 "unknown species '" + name + "'");
81 }
82
83 double value;
84 try {
85 value = fpValueCheck(ss.substr(valstart, stop-valstart));
86 } catch (CanteraError&) {
87 // If we have a key containing a colon, we expect this to fail. In
88 // this case, take the current substring as part of the key and look
89 // to the right of the next colon for the corresponding value.
90 // Otherwise, this is an invalid composition string.
91 std::string testname = ss.substr(start, stop-start);
92 if (testname.find_first_of(" \n\t") != npos) {
93 // Space, tab, and newline are never allowed in names
94 throw;
95 } else if (ss.substr(valstart, stop-valstart).find(':') != npos) {
96 left = colon + 1;
97 stop = 0; // Force another iteration of this loop
98 continue;
99 } else {
100 throw;
101 }
102 }
103 if (getValue(x, name, 0.0) != 0.0) {
104 throw CanteraError("parseCompString",
105 "Duplicate key: '" + name + "'.");
106 }
107
108 x[name] = value;
109 start = ss.find_first_not_of(", ;\n\t", stop+1);
110 left = start;
111 }
112 if (left != start) {
113 throw CanteraError("parseCompString", "Unable to parse key-value pair:"
114 "\n'{}'", ss.substr(start, stop));
115 }
116 if (stop != npos && !ba::trim_copy(ss.substr(stop)).empty()) {
117 throw CanteraError("parseCompString", "Found non-key:value data "
118 "in composition string: '" + ss.substr(stop) + "'");
119 }
120 return x;
121 }
122
intValue(const std::string & val)123 int intValue(const std::string& val)
124 {
125 return std::atoi(ba::trim_copy(val).c_str());
126 }
127
fpValue(const std::string & val)128 doublereal fpValue(const std::string& val)
129 {
130 doublereal rval;
131 std::stringstream ss(val);
132 ss.imbue(std::locale("C"));
133 ss >> rval;
134 return rval;
135 }
136
fpValueCheck(const std::string & val)137 doublereal fpValueCheck(const std::string& val)
138 {
139 std::string str = ba::trim_copy(val);
140 if (str.empty()) {
141 throw CanteraError("fpValueCheck", "string has zero length");
142 }
143 int numDot = 0;
144 int numExp = 0;
145 char ch;
146 int istart = 0;
147 ch = str[0];
148 if (ch == '+' || ch == '-') {
149 if (str.size() == 1) {
150 throw CanteraError("fpValueCheck", "string ends in '{}'", ch);
151 }
152 istart = 1;
153 }
154 for (size_t i = istart; i < str.size(); i++) {
155 ch = str[i];
156 if (isdigit(ch)) {
157 } else if (ch == '.') {
158 numDot++;
159 if (numDot > 1) {
160 throw CanteraError("fpValueCheck",
161 "string has more than one .");
162 }
163 if (numExp > 0) {
164 throw CanteraError("fpValueCheck",
165 "string has decimal point in exponent");
166 }
167 } else if (ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') {
168 numExp++;
169 str[i] = 'E';
170 if (numExp > 1) {
171 throw CanteraError("fpValueCheck",
172 "string has more than one exp char");
173 } else if (i == str.size() - 1) {
174 throw CanteraError("fpValueCheck",
175 "string ends in '{}'", ch);
176 }
177 ch = str[i+1];
178 if (ch == '+' || ch == '-') {
179 if (i + 1 == str.size() - 1) {
180 throw CanteraError("fpValueCheck",
181 "string ends in '{}'", ch);
182 }
183 i++;
184 }
185 } else {
186 throw CanteraError("fpValueCheck",
187 "Trouble processing string, " + str);
188 }
189 }
190 return fpValue(str);
191 }
192
parseSpeciesName(const std::string & nameStr,std::string & phaseName)193 std::string parseSpeciesName(const std::string& nameStr, std::string& phaseName)
194 {
195 std::string s = ba::trim_copy(nameStr);
196 phaseName = "";
197 size_t ibegin = s.find_first_not_of(" ;\n\t");
198 if (ibegin != std::string::npos) {
199 s = s.substr(ibegin,s.size());
200 size_t icolon = s.find(':');
201 size_t iend = s.find_first_of(" ;\n\t");
202 if (icolon != std::string::npos) {
203 phaseName = s.substr(0, icolon);
204 s = s.substr(icolon+1, s.size());
205 icolon = s.find(':');
206 if (icolon != std::string::npos) {
207 throw CanteraError("parseSpeciesName",
208 "two colons in name: '{}'", nameStr);
209 }
210 }
211 if (iend != std::string::npos) {
212 throw CanteraError("parseSpeciesName", "Species name has "
213 "\" ;/\n/\t\" in the middle of it: '{}'", nameStr);
214 }
215 }
216 return s;
217 }
218
strSItoDbl(const std::string & strSI)219 doublereal strSItoDbl(const std::string& strSI)
220 {
221 std::vector<std::string> v;
222 tokenizeString(strSI, v);
223 doublereal fp = 1.0;
224 size_t n = v.size();
225 if (n > 2 || n < 1) {
226 throw CanteraError("strSItoDbl",
227 "number of tokens is too high");
228 } else if (n == 2) {
229 fp = toSI(v[1]);
230 }
231 doublereal val = fpValueCheck(v[0]);
232 return val * fp;
233 }
234
tokenizeString(const std::string & in_val,std::vector<std::string> & v)235 void tokenizeString(const std::string& in_val, std::vector<std::string>& v)
236 {
237 std::string val = ba::trim_copy(in_val);
238 v.clear();
239 if (val.empty()) {
240 // In this case, prefer v to be empty instead of split's behavior of
241 // returning a vector with one element that is the empty string.
242 return;
243 }
244 ba::split(v, val, ba::is_space(), ba::token_compress_on);
245 }
246
copyString(const std::string & source,char * dest,size_t length)247 size_t copyString(const std::string& source, char* dest, size_t length)
248 {
249 const char* c_src = source.c_str();
250 size_t N = std::min(length, source.length()+1);
251 size_t ret = (length >= source.length() + 1) ? 0 : source.length() + 1;
252 std::copy(c_src, c_src + N, dest);
253 if (length != 0) {
254 dest[length-1] = '\0';
255 }
256 return ret;
257 }
258
trimCopy(const std::string & input)259 std::string trimCopy(const std::string &input) {
260 return ba::trim_copy(input);
261 }
262
toLowerCopy(const std::string & input)263 std::string toLowerCopy(const std::string &input) {
264 return ba::to_lower_copy(input);
265 }
266
caseInsensitiveEquals(const std::string & input,const std::string & test)267 bool caseInsensitiveEquals(const std::string &input, const std::string &test) {
268 return ba::iequals(input, test);
269 }
270
271 }
272