LCOV - code coverage report
Current view: top level - src/utils/common - StringUtils.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 179 209 85.6 %
Date: 2024-05-02 15:31:40 Functions: 25 30 83.3 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
       4             : // This program and the accompanying materials are made available under the
       5             : // terms of the Eclipse Public License 2.0 which is available at
       6             : // https://www.eclipse.org/legal/epl-2.0/
       7             : // This Source Code may also be made available under the following Secondary
       8             : // Licenses when the conditions for such availability set forth in the Eclipse
       9             : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10             : // or later which is available at
      11             : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12             : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13             : /****************************************************************************/
      14             : /// @file    StringUtils.cpp
      15             : /// @author  Daniel Krajzewicz
      16             : /// @author  Laura Bieker
      17             : /// @author  Michael Behrisch
      18             : /// @author  Robert Hilbrich
      19             : /// @date    unknown
      20             : ///
      21             : // Some static methods for string processing
      22             : /****************************************************************************/
      23             : #include <config.h>
      24             : 
      25             : #include <string>
      26             : #include <iostream>
      27             : #include <cstdio>
      28             : #include <cstring>
      29             : #include <regex>
      30             : #ifdef WIN32
      31             : #define NOMINMAX
      32             : #include <windows.h>
      33             : #undef NOMINMAX
      34             : #else
      35             : #include <unistd.h>
      36             : #endif
      37             : #include <xercesc/util/TransService.hpp>
      38             : #include <xercesc/util/TranscodingException.hpp>
      39             : #include <utils/common/UtilExceptions.h>
      40             : #include <utils/common/ToString.h>
      41             : #include <utils/common/StringTokenizer.h>
      42             : #include "StringUtils.h"
      43             : 
      44             : 
      45             : // ===========================================================================
      46             : // static member definitions
      47             : // ===========================================================================
      48             : std::string StringUtils::emptyString;
      49             : XERCES_CPP_NAMESPACE::XMLLCPTranscoder* StringUtils::myLCPTranscoder = nullptr;
      50             : 
      51             : 
      52             : // ===========================================================================
      53             : // method definitions
      54             : // ===========================================================================
      55             : std::string
      56     2679093 : StringUtils::prune(const std::string& str) {
      57             :     const std::string::size_type endpos = str.find_last_not_of(" \t\n\r");
      58     2679093 :     if (std::string::npos != endpos) {
      59     2677332 :         const int startpos = (int)str.find_first_not_of(" \t\n\r");
      60     2677332 :         return str.substr(startpos, endpos - startpos + 1);
      61             :     }
      62        1761 :     return "";
      63             : }
      64             : 
      65             : 
      66             : std::string
      67       57194 : StringUtils::pruneZeros(const std::string& str, int max) {
      68             :     const std::string::size_type endpos = str.find_last_not_of("0");
      69       57194 :     if (endpos != std::string::npos && str.back() == '0') {
      70       38165 :         std::string res = str.substr(0, MAX2((int)str.size() - max, (int)endpos + 1));
      71             :         return res;
      72             :     }
      73             :     return str;
      74             : }
      75             : 
      76             : std::string
      77     6649419 : StringUtils::to_lower_case(const std::string& str) {
      78             :     std::string s = str;
      79             :     std::transform(s.begin(), s.end(), s.begin(), [](char c) {
      80    38911309 :         return (char)::tolower(c);
      81             :     });
      82     6649419 :     return s;
      83             : }
      84             : 
      85             : 
      86             : std::string
      87        1710 : StringUtils::latin1_to_utf8(std::string str) {
      88             :     // inspired by http://stackoverflow.com/questions/4059775/convert-iso-8859-1-strings-to-utf-8-in-c-c
      89             :     std::string result;
      90        7129 :     for (const auto& c : str) {
      91        5419 :         const unsigned char uc = (unsigned char)c;
      92        5419 :         if (uc < 128) {
      93        5346 :             result += uc;
      94             :         } else {
      95          73 :             result += (char)(0xc2 + (uc > 0xbf));
      96          73 :             result += (char)((uc & 0x3f) + 0x80);
      97             :         }
      98             :     }
      99        1710 :     return result;
     100             : }
     101             : 
     102             : 
     103             : std::string
     104     1355048 : StringUtils::convertUmlaute(std::string str) {
     105     4065144 :     str = replace(str, "\xE4", "ae");
     106     4065144 :     str = replace(str, "\xC4", "Ae");
     107     4065144 :     str = replace(str, "\xF6", "oe");
     108     4065144 :     str = replace(str, "\xD6", "Oe");
     109     4065144 :     str = replace(str, "\xFC", "ue");
     110     4065144 :     str = replace(str, "\xDC", "Ue");
     111     4065144 :     str = replace(str, "\xDF", "ss");
     112     4065144 :     str = replace(str, "\xC9", "E");
     113     4065144 :     str = replace(str, "\xE9", "e");
     114     4065144 :     str = replace(str, "\xC8", "E");
     115     4065144 :     str = replace(str, "\xE8", "e");
     116     1355048 :     return str;
     117             : }
     118             : 
     119             : 
     120             : std::string
     121    54479912 : StringUtils::replace(std::string str, const std::string& what, const std::string& by) {
     122             :     std::string::size_type idx = str.find(what);
     123    54479912 :     const int what_len = (int)what.length();
     124    54479912 :     if (what_len > 0) {
     125    54479910 :         const int by_len = (int)by.length();
     126    54482972 :         while (idx != std::string::npos) {
     127        3062 :             str = str.replace(idx, what_len, by);
     128        3062 :             idx = str.find(what, idx + by_len);
     129             :         }
     130             :     }
     131    54479912 :     return str;
     132             : }
     133             : 
     134             : 
     135             : std::string
     136      999144 : StringUtils::substituteEnvironment(const std::string& str, const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
     137             :     std::string s = str;
     138      999144 :     if (timeRef != nullptr) {
     139             :         const std::string::size_type localTimeIndex = str.find("${LOCALTIME}");
     140             :         const std::string::size_type utcIndex = str.find("${UTC}");
     141             :         const bool isUTC = utcIndex != std::string::npos;
     142      999138 :         if (localTimeIndex != std::string::npos || isUTC) {
     143          12 :             const time_t rawtime = std::chrono::system_clock::to_time_t(*timeRef);
     144             :             char buffer [80];
     145          12 :             struct tm* timeinfo = isUTC ? gmtime(&rawtime) : localtime(&rawtime);
     146          12 :             strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S.", timeinfo);
     147             :             auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(*timeRef);
     148             :             auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(*timeRef - seconds);
     149          12 :             const std::string micro = buffer + toString(microseconds.count());
     150          12 :             if (isUTC) {
     151             :                 s.replace(utcIndex, 6, micro);
     152             :             } else {
     153             :                 s.replace(localTimeIndex, 12, micro);
     154             :             }
     155             :         }
     156             :     }
     157             :     const std::string::size_type pidIndex = str.find("${PID}");
     158      999144 :     if (pidIndex != std::string::npos) {
     159             : #ifdef WIN32
     160             :         s.replace(pidIndex, 6, toString(::GetCurrentProcessId()));
     161             : #else
     162          12 :         s.replace(pidIndex, 6, toString(::getpid()));
     163             : #endif
     164             :     }
     165      999144 :     if (std::getenv("SUMO_LOGO") == nullptr) {
     166     3996576 :         s = replace(s, "${SUMO_LOGO}", "${SUMO_HOME}/data/logo/sumo-128x138.png");
     167             :     }
     168             :     const std::string::size_type tildeIndex = str.find("~");
     169      999144 :     if (tildeIndex == 0) {
     170           6 :         s.replace(0, 1, "${HOME}");
     171             :     }
     172     2997432 :     s = replace(s, ",~", ",${HOME}");
     173             : #ifdef WIN32
     174             :     if (std::getenv("HOME") == nullptr) {
     175             :         s = replace(s, "${HOME}", "${USERPROFILE}");
     176             :     }
     177             : #endif
     178             : 
     179             :     // Expression for an environment variables, e.g. ${NAME}
     180             :     // Note: - R"(...)" is a raw string literal syntax to simplify a regex declaration
     181             :     //       - .+? looks for the shortest match (non-greedy)
     182             :     //       - (.+?) defines a "subgroup" which is already stripped of the $ and {, }
     183      999144 :     std::regex envVarExpr(R"(\$\{(.+?)\})");
     184             : 
     185             :     // Are there any variables in this string?
     186             :     std::smatch match;
     187             :     std::string strIter = s;
     188             : 
     189             :     // Loop over the entire value string and look for variable names
     190      999250 :     while (std::regex_search(strIter, match, envVarExpr)) {
     191             :         std::string varName = match[1];
     192             : 
     193             :         // Find the variable in the environment and its value
     194             :         std::string varValue;
     195         106 :         if (std::getenv(varName.c_str()) != nullptr) {
     196         106 :             varValue = std::getenv(varName.c_str());
     197             :         }
     198             : 
     199             :         // Replace the variable placeholder with its value in the original string
     200         216 :         s = std::regex_replace(s, std::regex("\\$\\{" + varName + "\\}"), varValue);
     201             : 
     202             :         // Continue the loop with the remainder of the string
     203         212 :         strIter = match.suffix();
     204             :     }
     205      999144 :     return s;
     206      999144 : }
     207             : 
     208             : 
     209             : bool
     210     4665230 : StringUtils::startsWith(const std::string& str, const std::string prefix) {
     211     4665230 :     return str.compare(0, prefix.length(), prefix) == 0;
     212             : }
     213             : 
     214             : 
     215             : bool
     216      299059 : StringUtils::endsWith(const std::string& str, const std::string suffix) {
     217      299059 :     if (str.length() >= suffix.length()) {
     218      299037 :         return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
     219             :     } else {
     220             :         return false;
     221             :     }
     222             : }
     223             : 
     224             : 
     225             : std::string
     226           0 : StringUtils::padFront(const std::string& str, int length, char padding) {
     227           0 :     return std::string(MAX2(0, length - (int)str.size()), padding) + str;
     228             : }
     229             : 
     230             : 
     231             : std::string
     232     1031040 : StringUtils::escapeXML(const std::string& orig, const bool maskDoubleHyphen) {
     233     3093120 :     std::string result = replace(orig, "&", "&amp;");
     234     3093120 :     result = replace(result, ">", "&gt;");
     235     3093120 :     result = replace(result, "<", "&lt;");
     236     3093120 :     result = replace(result, "\"", "&quot;");
     237     1031040 :     if (maskDoubleHyphen) {
     238     1372230 :         result = replace(result, "--", "&#45;&#45;");
     239             :     }
     240    32993280 :     for (char invalid = '\1'; invalid < ' '; invalid++) {
     241    95886720 :         result = replace(result, std::string(1, invalid).c_str(), "");
     242             :     }
     243     3243153 :     return replace(result, "'", "&apos;");
     244             : }
     245             : 
     246             : 
     247             : std::string
     248        6814 : StringUtils::urlEncode(const std::string& toEncode, const std::string encodeWhich) {
     249        6814 :     std::ostringstream out;
     250             : 
     251      180906 :     for (int i = 0; i < (int)toEncode.length(); ++i) {
     252      174092 :         const char t = toEncode.at(i);
     253             : 
     254      174093 :         if ((encodeWhich != "" && encodeWhich.find(t) == std::string::npos) ||
     255           0 :                 (encodeWhich == "" &&
     256           0 :                  ((t >= 45 && t <= 57) ||       // hyphen, period, slash, 0-9
     257           0 :                   (t >= 65 && t <= 90) ||        // A-Z
     258             :                   t == 95 ||                     // underscore
     259             :                   (t >= 97 && t <= 122) ||       // a-z
     260             :                   t == 126))                     // tilde
     261             :            ) {
     262      174091 :             out << toEncode.at(i);
     263             :         } else {
     264           2 :             out << charToHex(toEncode.at(i));
     265             :         }
     266             :     }
     267             : 
     268        6814 :     return out.str();
     269        6814 : }
     270             : 
     271             : 
     272             : std::string
     273       61343 : StringUtils::urlDecode(const std::string& toDecode) {
     274       61343 :     std::ostringstream out;
     275             : 
     276      962638 :     for (int i = 0; i < (int)toDecode.length(); ++i) {
     277      901298 :         if (toDecode.at(i) == '%') {
     278           3 :             std::string str(toDecode.substr(i + 1, 2));
     279           3 :             out << hexToChar(str);
     280           0 :             i += 2;
     281             :         } else {
     282      901295 :             out << toDecode.at(i);
     283             :         }
     284             :     }
     285             : 
     286       61340 :     return out.str();
     287       61343 : }
     288             : 
     289             : std::string
     290           1 : StringUtils::charToHex(unsigned char c) {
     291             :     short i = c;
     292             : 
     293           1 :     std::stringstream s;
     294             : 
     295           1 :     s << "%" << std::setw(2) << std::setfill('0') << std::hex << i;
     296             : 
     297           1 :     return s.str();
     298           1 : }
     299             : 
     300             : 
     301             : unsigned char
     302           3 : StringUtils::hexToChar(const std::string& str) {
     303           3 :     short c = 0;
     304           3 :     if (!str.empty()) {
     305           3 :         std::istringstream in(str);
     306           3 :         in >> std::hex >> c;
     307           3 :         if (in.fail()) {
     308           6 :             throw NumberFormatException(str + " could not be interpreted as hex");
     309             :         }
     310           3 :     }
     311           0 :     return static_cast<unsigned char>(c);
     312             : }
     313             : 
     314             : 
     315             : int
     316    16793492 : StringUtils::toInt(const std::string& sData) {
     317    16793492 :     long long int result = toLong(sData);
     318    16783587 :     if (result > std::numeric_limits<int>::max() || result < std::numeric_limits<int>::min()) {
     319           4 :         throw NumberFormatException(toString(result) + " int overflow");
     320             :     }
     321    16783585 :     return (int)result;
     322             : }
     323             : 
     324             : 
     325             : int
     326           0 : StringUtils::toIntSecure(const std::string& sData, int def) {
     327           0 :     if (sData.length() == 0) {
     328             :         return def;
     329             :     }
     330           0 :     return toInt(sData);
     331             : }
     332             : 
     333             : 
     334             : long long int
     335    18959663 : StringUtils::toLong(const std::string& sData) {
     336             :     const char* const data = sData.c_str();
     337    18959663 :     if (data == 0 || data[0] == 0) {
     338          70 :         throw EmptyData();
     339             :     }
     340             :     char* end;
     341    18959593 :     errno = 0;
     342             : #ifdef WIN32
     343             :     long long int ret = _strtoi64(data, &end, 10);
     344             : #else
     345    18959593 :     long long int ret = strtoll(data, &end, 10);
     346             : #endif
     347    18959593 :     if (errno == ERANGE) {
     348           0 :         errno = 0;
     349           0 :         throw NumberFormatException("(long long integer range) " + sData);
     350             :     }
     351    18959593 :     if ((int)(end - data) != (int)strlen(data)) {
     352       43072 :         throw NumberFormatException("(long long integer format) " + sData);
     353             :     }
     354    18938057 :     return ret;
     355             : }
     356             : 
     357             : 
     358             : int
     359        1176 : StringUtils::hexToInt(const std::string& sData) {
     360        1176 :     if (sData.length() == 0) {
     361           0 :         throw EmptyData();
     362             :     }
     363        1176 :     size_t idx = 0;
     364             :     int result;
     365             :     try {
     366        1176 :         if (sData[0] == '#') { // for html color codes
     367        1092 :             result = std::stoi(sData.substr(1), &idx, 16);
     368        1092 :             idx++;
     369             :         } else {
     370             :             result = std::stoi(sData, &idx, 16);
     371             :         }
     372           0 :     } catch (...) {
     373           0 :         throw NumberFormatException("(hex integer format) " + sData);
     374           0 :     }
     375        1176 :     if (idx != sData.length()) {
     376           0 :         throw NumberFormatException("(hex integer format) " + sData);
     377             :     }
     378        1176 :     return result;
     379             : }
     380             : 
     381             : 
     382             : double
     383    61847073 : StringUtils::toDouble(const std::string& sData) {
     384    61847073 :     if (sData.size() == 0) {
     385         207 :         throw EmptyData();
     386             :     }
     387             :     try {
     388    61846866 :         size_t idx = 0;
     389             :         const double result = std::stod(sData, &idx);
     390    61845205 :         if (idx != sData.size()) {
     391         122 :             throw NumberFormatException("(double format) " + sData);
     392             :         } else {
     393    61845144 :             return result;
     394             :         }
     395        1722 :     } catch (...) {
     396             :         // invalid_argument or out_of_range
     397        3444 :         throw NumberFormatException("(double) " + sData);
     398        1722 :     }
     399             : }
     400             : 
     401             : 
     402             : double
     403         352 : StringUtils::toDoubleSecure(const std::string& sData, const double def) {
     404         352 :     if (sData.length() == 0) {
     405             :         return def;
     406             :     }
     407         352 :     return toDouble(sData);
     408             : }
     409             : 
     410             : 
     411             : bool
     412     3264844 : StringUtils::toBool(const std::string& sData) {
     413     3264844 :     if (sData.length() == 0) {
     414           2 :         throw EmptyData();
     415             :     }
     416     3264842 :     const std::string s = to_lower_case(sData);
     417    13541236 :     if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
     418     1887781 :         return true;
     419             :     }
     420     1839189 :     if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
     421     1377004 :         return false;
     422             :     }
     423          57 :     throw BoolFormatException(s);
     424             : }
     425             : 
     426             : MMVersion
     427       41351 : StringUtils::toVersion(const std::string& sData) {
     428      124053 :     std::vector<std::string> parts = StringTokenizer(sData, ".").getVector();
     429       82702 :     return MMVersion(toInt(parts.front()), toDouble(parts.back()));
     430       41351 : }
     431             : 
     432             : std::string
     433    94162749 : StringUtils::transcode(const XMLCh* const data, int length) {
     434    94162749 :     if (data == 0) {
     435           0 :         throw EmptyData();
     436             :     }
     437    94162749 :     if (length == 0) {
     438      540811 :         return "";
     439             :     }
     440             : #if _XERCES_VERSION < 30100
     441             :     char* t = XERCES_CPP_NAMESPACE::XMLString::transcode(data);
     442             :     std::string result(t);
     443             :     XERCES_CPP_NAMESPACE::XMLString::release(&t);
     444             :     return result;
     445             : #else
     446             :     try {
     447    93621938 :         XERCES_CPP_NAMESPACE::TranscodeToStr utf8(data, "UTF-8");
     448    93621938 :         return reinterpret_cast<const char*>(utf8.str());
     449    93621938 :     } catch (XERCES_CPP_NAMESPACE::TranscodingException&) {
     450           0 :         return "?";
     451           0 :     }
     452             : #endif
     453             : }
     454             : 
     455             : 
     456             : std::string
     457      634577 : StringUtils::transcodeFromLocal(const std::string& localString) {
     458             : #if _XERCES_VERSION > 30100
     459             :     try {
     460      634577 :         if (myLCPTranscoder == nullptr) {
     461       47412 :             myLCPTranscoder = XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
     462             :         }
     463      634577 :         if (myLCPTranscoder != nullptr) {
     464      634577 :             return transcode(myLCPTranscoder->transcode(localString.c_str()));
     465             :         }
     466           0 :     } catch (XERCES_CPP_NAMESPACE::TranscodingException&) {}
     467             : #endif
     468             :     return localString;
     469             : }
     470             : 
     471             : 
     472             : std::string
     473      614777 : StringUtils::transcodeToLocal(const std::string& utf8String) {
     474             : #if _XERCES_VERSION > 30100
     475             :     try {
     476      614777 :         if (myLCPTranscoder == nullptr) {
     477        1018 :             myLCPTranscoder = XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
     478             :         }
     479      614777 :         if (myLCPTranscoder != nullptr) {
     480      614777 :             XERCES_CPP_NAMESPACE::TranscodeFromStr utf8(reinterpret_cast<const XMLByte*>(utf8String.c_str()), utf8String.size(), "UTF-8");
     481      614777 :             return myLCPTranscoder->transcode(utf8.str());
     482      614777 :         }
     483           0 :     } catch (XERCES_CPP_NAMESPACE::TranscodingException&) {}
     484             : #endif
     485             :     return utf8String;
     486             : }
     487             : 
     488             : 
     489             : std::string
     490           0 : StringUtils::trim_left(const std::string s, const std::string& t) {
     491             :     std::string result = s;
     492           0 :     result.erase(0, s.find_first_not_of(t));
     493           0 :     return result;
     494             : }
     495             : 
     496             : std::string
     497           0 : StringUtils::trim_right(const std::string s, const std::string& t) {
     498             :     std::string result = s;
     499           0 :     result.erase(s.find_last_not_of(t) + 1);
     500           0 :     return result;
     501             : }
     502             : 
     503             : std::string
     504           0 : StringUtils::trim(const std::string s, const std::string& t) {
     505           0 :     return trim_right(trim_left(s, t), t);
     506             : }
     507             : 
     508             : void
     509       48907 : StringUtils::resetTranscoder() {
     510       48907 :     myLCPTranscoder = nullptr;
     511       48907 : }
     512             : 
     513             : /****************************************************************************/

Generated by: LCOV version 1.14