/** * @file stringUtils.cpp * Contains definitions for string manipulation functions * within Cantera. */ /* * $Id$ */ // Copyright 2001 California Institute of Technology //@{ #ifdef WIN32 #pragma warning(disable:4786) #pragma warning(disable:4503) #pragma warning(disable:4996) #define SNPRINTF _snprintf #else #define SNPRINTF snprintf #endif //@} #include "ct_defs.h" #include "stringUtils.h" #include "ctexceptions.h" #include "global.h" #include "ctml.h" #include #include #include #include #include namespace Cantera { // Convert a double into a c++ string /* * This routine doesn't assume a formatting. You * must supply the formatting * * @param x double to be converted * @param fmt Format to be used (printf style) */ std::string fp2str(const double x, const std::string &fmt) { char buf[64]; int n = SNPRINTF(buf, 63, fmt.c_str(), x); if (n > 0) { buf[63] = '\0'; return std::string(buf); } return std::string(" "); } std::string fp2str(const double x) { char buf[64]; int n = SNPRINTF(buf, 64, "%g" , x); if (n > 0) { buf[29] = '\0'; return std::string(buf); } return std::string(" "); } /* * Convert an integer number to a std::string using sprintf. */ std::string int2str(const int n, const std::string &fmt) { char buf[30]; int m = SNPRINTF(buf, 30, fmt.c_str(), n); if (m > 0) { buf[29] = '\0'; return std::string(buf); } return std::string(" "); } // Convert an int to a string /* * @param n int to be converted */ std::string int2str(const int n) { char buf[30]; int m = SNPRINTF(buf, 30, "%d", n); if (m > 0) { buf[29] = '\0'; return std::string(buf); } return std::string(" "); } std::string lowercase(const std::string &s) { int n = static_cast(s.size()); std::string lc(s); for (int i = 0; i < n; i++) lc[i] = tolower(s[i]); return lc; } //! Return the position of the first printable //! character in the string /*! * @param s input string * @return Returns an int representing the first * printable string. If none returns the * size of the string. */ static int firstChar(const std::string &s) { int i; int n = static_cast(s.size()); for (i = 0; i < n; i++) { if (s[i] != ' ' && isprint(s[i])) break; } return i; } //! Return the position of the last printable //! character in the string /*! * @param s input string * @return Returns an int representing the first * printable string. If none returns * -1. */ static int lastChar(const std::string &s) { int i; int n = static_cast(s.size()); for (i = n-1; i >= 0; i--) if (s[i] != ' ' && isprint(s[i])) break; return i; } // Strip the leading and trailing white space // from a string /* * The command isprint() is used to determine printable * characters. * * @param s Input string * @return Returns a copy of the string, stripped * of leading and trailing white space */ std::string stripws(const std::string &s) { int ifirst = firstChar(s); int ilast = lastChar(s); return s.substr(ifirst, ilast - ifirst + 1); } // Strip non-printing characters wherever they are /* * @param s Input string * @return Returns a copy of the string, * stripped of all non-printing characters. */ std::string stripnonprint(const std::string &s) { int i; int n = static_cast(s.size()); std::string ss = ""; for (i = 0; i < n; i++) { if (isprint(s[i])) { ss += s[i]; } } return ss; } // Parse a composition string into a map consisting of individual key:composition // pairs. /* * The composition is a double. * Example * * Input is * * "fire:0 ice:1 snow:2" * * Output is * x["fire"] = 0 * x["ice"] = 1 * x["snow"] = 2 * * @param ss original string consisting of multiple key:composition * pairs on multiple lines * @param x Output map consisting of a composition * map, which is a string to double map */ void parseCompString(const std::string &ss, Cantera::compositionMap& x) { std::string s = ss; std::string::size_type icolon, ibegin, iend; std::string name, num, nm; do { ibegin = s.find_first_not_of(", ;\n\t"); if (ibegin != std::string::npos) { s = s.substr(ibegin,s.size()); icolon = s.find(':'); iend = s.find_first_of(", ;\n\t"); //icomma = s.find(','); if (icolon != std::string::npos) { name = s.substr(0, icolon); if (iend != std::string::npos) { num = s.substr(icolon+1, iend-icolon); s = s.substr(iend+1, s.size()); } else { num = s.substr(icolon+1, s.size()); s = ""; } nm = stripws(name); if (x.find(nm) == x.end()) { throw CanteraError("parseCompString", "unknown species " + nm); } x[nm] = atof(num.c_str()); } else s = ""; } } while (s != ""); } // Parse a composition string into individual key:composition // pairs /* * * @param ss original string consisting of multiple key:composition * pairs on multiple lines * @param w Output vector consisting of single key:composition * items in each index. */ void split(const std::string &ss, std::vector& w) { std::string s = ss; std::string::size_type ibegin, iend; std::string name, num, nm; do { ibegin = s.find_first_not_of(", ;\n\t"); if (ibegin != std::string::npos) { s = s.substr(ibegin,s.size()); iend = s.find_first_of(", ;\n\t"); if (iend != std::string::npos) { w.push_back(s.substr(0, iend)); s = s.substr(iend+1, s.size()); } else { w.push_back(s.substr(0, s.size())); return; } } } while (s != ""); } int fillArrayFromString(const std::string& str, doublereal* const a, const char delim) { std::string::size_type iloc; int count = 0; std::string num; std::string s = str; while (s.size() > 0) { iloc = s.find(delim); if (iloc > 0) { num = s.substr(0, iloc); s = s.substr(iloc+1,s.size()); } else { num = s; s = ""; } a[count] = atofCheck(num.c_str()); count++; } return count; } // Get the file name without the path or extension /* * @param fullPath Input file name consisting * of the full file name * * @return Returns the basename */ std::string getBaseName(const std::string& path) { std::string file; size_t idot = path.find_last_of('.'); size_t islash = path.find_last_of('/'); if (idot > 0 && idot < path.size()) { if (islash > 0 && islash < idot) { file = path.substr(islash+1, idot-islash-1); } else { file = path.substr(0,idot); } } else { file = path; } return file; } int intValue(std::string val) { return std::atoi(stripws(val).c_str()); } doublereal fpValue(std::string val) { return std::atof(stripws(val).c_str()); } doublereal fpValueCheck(std::string val) { return atofCheck(stripws(val).c_str()); } // Generate a logfile name based on an input file name /* * It tries to find the basename. Then, it appends a .log * to it. * * @param infile Input file name * * @return Returns a logfile name */ std::string logfileName(const std::string& infile) { std::string logfile = getBaseName(infile); logfile += ".log"; return logfile; } // Line wrap a string via a copy operation /* * @param s Input string to be line wrapped * @paramlen Length at which to wrap. The * default is 70. */ std::string wrapString(const std::string& s, const int len) { int nc = s.size(); int n, count=0; std::string r; for (n = 0; n < nc; n++) { if (s[n] == '\n') count = 0; else count++; if (count > len && s[n] == ' ') { r += "\n "; count = 0; } r += s[n]; } return r; } // Routine strips off white space from a c character string /* * This routine strips off blanks and tabs (only leading and trailing * characters) in 'str'. On return, it returns the number of * characters still included in the string (excluding the null character). * * Comments are excluded -> All instances of the comment character, '!', * are replaced by '\0' thereby terminating * the string * * Parameter list: * * @param str On output 'str' contains the same characters as on * input except the leading and trailing white space and * comments have been removed. */ int stripLTWScstring(char str[]) { int i = 0, j = 0; char ch; const char COM_CHAR='\0'; /* * Quick Returns */ if ((str == 0) || (str[0] == '\0')) return (0); /* Find first non-space character character */ while(((ch = str[i]) != '\0') && isspace(ch)) i++; /* * Move real part of str to the front by copying the string * - Comments are handled here, by terminating the copy at the * first comment indicator, and inserting the null character at * that point. */ while ( (ch = str[j+i]) != '\0' && (ch != COM_CHAR)) { str[j] = ch; j++; } str[j] = '\0'; j--; /* Remove trailing white space by inserting a null character */ while( (j != -1 ) && isspace(str[j])) j--; j++; str[j] = '\0'; return (j); } // Translate a char string into a single double /* * atofCheck is a wrapper around the C stdlib routine atof(). * It does quite a bit more error checking than atof() or * strtod(), and is quite a bit more restrictive. * * First it interprets both E, e, d, and D as exponents. * atof() only interprets e or E as an exponent character. * * It only accepts a string as well formed if it consists as a * single token. Multiple words will produce an error message * * It will produce an error for NAN and inf entries as well, * in contrast to atof() or strtod(). * The user needs to know that a serious numerical issue * has occurred. * * It does not accept hexadecimal numbers. * * @param dptr pointer to the input c string * @return Returns the double * * On any error, it will throw a CanteraError signal. */ doublereal atofCheck(const char * const dptr) { if (!dptr) { throw CanteraError("atofCheck", "null pointer to string"); } char *eptr = (char *) malloc(strlen(dptr)+1); strcpy(eptr, dptr); int ll = stripLTWScstring(eptr); if (ll == 0) { throw CanteraError("atofCheck", "string has zero length"); } int numDot = 0; int numExp = 0; char ch; int istart = 0; ch = eptr[0]; if (ch == '+' || ch == '-') { istart = 1; } for (int i = istart; i < ll; i++) { ch = eptr[i]; if (isdigit(ch)) { } else if (ch == '.') { numDot++; if (numDot > 1) { free(eptr); throw CanteraError("atofCheck", "string has more than one ."); } } else if (ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') { numExp++; eptr[i] = 'E'; if (numExp > 1) { free(eptr); throw CanteraError("atofCheck", "string has more than one exp char"); } ch = eptr[i+1]; if (ch == '+' || ch == '-') { i++; } } else { std::string hh(dptr); free(eptr); throw CanteraError("atofCheck", "Trouble processing string, " + hh); } } doublereal rval = atof(eptr); free(eptr); return rval; } // Interpret one or two token string as a single double /* * This is similar to atof(). However, the second token * is interpreted as an MKS units string and a conversion * factor to MKS is applied. * * Example * " 1.0 atm" * * results in the number 1.01325e5 * * @param strSI string to be converted. One or two tokens * * @return returns a converted double */ doublereal strSItoDbl(const std::string& strSI) { std::vector v; tokenizeString(strSI, v); doublereal fp = 1.0; int n = v.size(); if (n > 2 || n < 1) { throw CanteraError("strSItoDbl", "number of tokens is too high"); } else if (n == 2) { fp = toSI(v[1]); } doublereal val = atofCheck(v[0].c_str()); return (val * fp); } static std::string::size_type findFirstWS(const std::string& val) { std::string::size_type ibegin = std::string::npos; int j = 0; std::string::const_iterator i = val.begin(); for ( ; i != val.end(); i++) { char ch = *i; int ll = (int) ch; if (isspace(ll)) { ibegin = (std::string::size_type) j; break; } j++; } return ibegin; } static std::string::size_type findFirstNotOfWS(const std::string& val) { std::string::size_type ibegin = std::string::npos; int j = 0; std::string::const_iterator i = val.begin(); for ( ; i != val.end(); i++) { char ch = *i; int ll = (int) ch; if (!isspace(ll)) { ibegin = (std::string::size_type) j; break; } j++; } return ibegin; } // This function separates a string up into tokens // according to the location of white space. /* * The separate tokens are returned in a string vector, v. * * @param oval String to be broken up * @param v Output vector of tokens. */ void tokenizeString(const std::string& oval, std::vector& v) { std::string val(oval); std::string::size_type ibegin, iend; v.clear(); while (1 > 0) { ibegin = findFirstNotOfWS(val); if (ibegin != std::string::npos) { val = val.substr(ibegin,val.size()); iend = findFirstWS(val); if (iend == std::string::npos) { v.push_back(val); break; } else { v.push_back(val.substr(0,iend)); val = val.substr(iend+1,val.size()); } } else { break; } } } }