/* * Copyright (C) 2005-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, see * . * */ //----------------------------------------------------------------------- // // File: utils/StringUtils.cpp // // Purpose: ATL split string utility // Author: Paul J. Weiss // // Modified to use J O'Leary's CStdString class by kraqh3d // //------------------------------------------------------------------------ #include "utils/StringUtils.h" #include #include using namespace std; /* empty string for use in returns by ref */ const CStdString StringUtils::EmptyString = ""; void StringUtils::JoinString(const CStdStringArray &strings, const CStdString& delimiter, CStdString& result) { result = ""; for(CStdStringArray::const_iterator it = strings.begin(); it != strings.end(); it++ ) result += (*it) + delimiter; if(result != "") result.Delete(result.size()-delimiter.size(), delimiter.size()); } CStdString StringUtils::JoinString(const CStdStringArray &strings, const CStdString& delimiter) { CStdString result; JoinString(strings, delimiter, result); return result; } CStdString StringUtils::Join(const vector &strings, const CStdString& delimiter) { CStdStringArray strArray; for (unsigned int index = 0; index < strings.size(); index++) strArray.push_back(strings.at(index)); return JoinString(strArray, delimiter); } // Splits the string input into pieces delimited by delimiter. // if 2 delimiters are in a row, it will include the empty string between them. // added MaxStrings parameter to restrict the number of returned substrings (like perl and python) int StringUtils::SplitString(const CStdString& input, const CStdString& delimiter, CStdStringArray &results, unsigned int iMaxStrings /* = 0 */) { int iPos = -1; int newPos = -1; int sizeS2 = delimiter.GetLength(); int isize = input.GetLength(); results.clear(); vector positions; newPos = input.Find (delimiter, 0); if ( newPos < 0 ) { results.push_back(input); return 1; } while ( newPos > iPos ) { positions.push_back(newPos); iPos = newPos; newPos = input.Find (delimiter, iPos + sizeS2); } // numFound is the number of delimeters which is one less // than the number of substrings unsigned int numFound = positions.size(); if (iMaxStrings > 0 && numFound >= iMaxStrings) numFound = iMaxStrings - 1; for ( unsigned int i = 0; i <= numFound; i++ ) { CStdString s; if ( i == 0 ) { if ( i == numFound ) s = input; else s = input.Mid( i, positions[i] ); } else { int offset = positions[i - 1] + sizeS2; if ( offset < isize ) { if ( i == numFound ) s = input.Mid(offset); else if ( i > 0 ) s = input.Mid( positions[i - 1] + sizeS2, positions[i] - positions[i - 1] - sizeS2 ); } } results.push_back(s); } // return the number of substrings return results.size(); } CStdStringArray StringUtils::SplitString(const CStdString& input, const CStdString& delimiter, unsigned int iMaxStrings /* = 0 */) { CStdStringArray result; SplitString(input, delimiter, result, iMaxStrings); return result; } vector StringUtils::Split(const CStdString& input, const CStdString& delimiter, unsigned int iMaxStrings /* = 0 */) { CStdStringArray result; SplitString(input, delimiter, result, iMaxStrings); vector strArray; for (unsigned int index = 0; index < result.size(); index++) strArray.push_back(result.at(index)); return strArray; } // returns the number of occurences of strFind in strInput. int StringUtils::FindNumber(const CStdString& strInput, const CStdString &strFind) { int pos = strInput.Find(strFind, 0); int numfound = 0; while (pos > 0) { numfound++; pos = strInput.Find(strFind, pos + 1); } return numfound; } // Compares separately the numeric and alphabetic parts of a string. // returns negative if left < right, positive if left > right // and 0 if they are identical (essentially calculates left - right) int64_t StringUtils::AlphaNumericCompare(const char *left, const char *right) { unsigned char *l = (unsigned char *)left; unsigned char *r = (unsigned char *)right; unsigned char *ld, *rd; unsigned char lc, rc; int64_t lnum, rnum; while (*l != 0 && *r != 0) { // check if we have a numerical value if (*l >= '0' && *l <= '9' && *r >= '0' && *r <= '9') { ld = l; lnum = 0; while (*ld >= '0' && *ld <= '9' && ld < l + 15) { // compare only up to 15 digits lnum *= 10; lnum += *ld++ - '0'; } rd = r; rnum = 0; while (*rd >= '0' && *rd <= '9' && rd < r + 15) { // compare only up to 15 digits rnum *= 10; rnum += *rd++ - '0'; } // do we have numbers? if (lnum != rnum) { // yes - and they're different! return lnum - rnum; } l = ld; r = rd; continue; } // do case less comparison lc = *l; if (lc >= 'A' && lc <= 'Z') lc += 'a'-'A'; rc = *r; if (rc >= 'A' && rc <= 'Z') rc += 'a'-'A'; // ok, do a normal comparison. Add special case stuff (eg '(' characters)) in here later if (lc != rc) { return lc - rc; } l++; r++; } if (*r) { // r is longer return -1; } else if (*l) { // l is longer return 1; } return 0; // files are the same } int StringUtils::DateStringToYYYYMMDD(const CStdString &dateString) { CStdStringArray days; int splitCount = StringUtils::SplitString(dateString, "-", days); if (splitCount == 1) return atoi(days[0].c_str()); else if (splitCount == 2) return atoi(days[0].c_str())*100+atoi(days[1].c_str()); else if (splitCount == 3) return atoi(days[0].c_str())*10000+atoi(days[1].c_str())*100+atoi(days[2].c_str()); else return -1; } long StringUtils::TimeStringToSeconds(const CStdString &timeString) { if(timeString.Right(4).Equals(" min")) { // this is imdb format of "XXX min" return 60 * atoi(timeString.c_str()); } else { CStdStringArray secs; StringUtils::SplitString(timeString, ":", secs); int timeInSecs = 0; for (unsigned int i = 0; i < secs.size(); i++) { timeInSecs *= 60; timeInSecs += atoi(secs[i]); } return timeInSecs; } } CStdString StringUtils::SecondsToTimeString(long lSeconds, TIME_FORMAT format) { int hh = lSeconds / 3600; lSeconds = lSeconds % 3600; int mm = lSeconds / 60; int ss = lSeconds % 60; if (format == TIME_FORMAT_GUESS) format = (hh >= 1) ? TIME_FORMAT_HH_MM_SS : TIME_FORMAT_MM_SS; CStdString strHMS; if (format & TIME_FORMAT_HH) strHMS.AppendFormat("%02.2i", hh); else if (format & TIME_FORMAT_H) strHMS.AppendFormat("%i", hh); if (format & TIME_FORMAT_MM) strHMS.AppendFormat(strHMS.IsEmpty() ? "%02.2i" : ":%02.2i", mm); if (format & TIME_FORMAT_SS) strHMS.AppendFormat(strHMS.IsEmpty() ? "%02.2i" : ":%02.2i", ss); return strHMS; } bool StringUtils::IsNaturalNumber(const CStdString& str) { if (0 == (int)str.size()) return false; for (int i = 0; i < (int)str.size(); i++) { if ((str[i] < '0') || (str[i] > '9')) return false; } return true; } bool StringUtils::IsInteger(const CStdString& str) { if (str.size() > 0 && str[0] == '-') return IsNaturalNumber(str.Mid(1)); else return IsNaturalNumber(str); } void StringUtils::RemoveCRLF(CStdString& strLine) { while ( strLine.size() && (strLine.Right(1) == "\n" || strLine.Right(1) == "\r") ) { strLine = strLine.Left(std::max(0, (int)strLine.size() - 1)); } } CStdString StringUtils::SizeToString(int64_t size) { CStdString strLabel; const char prefixes[] = {' ','k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'}; unsigned int i = 0; double s = (double)size; while (i < sizeof(prefixes)/sizeof(prefixes[0]) && s >= 1000.0) { s /= 1024.0; i++; } if (!i) strLabel.Format("%.0lf %cB ", s, prefixes[i]); else if (s >= 100.0) strLabel.Format("%.1lf %cB", s, prefixes[i]); else strLabel.Format("%.2lf %cB", s, prefixes[i]); return strLabel; } size_t StringUtils::FindWords(const char *str, const char *wordLowerCase) { // NOTE: This assumes word is lowercase! unsigned char *s = (unsigned char *)str; do { // start with a compare unsigned char *c = s; unsigned char *w = (unsigned char *)wordLowerCase; bool same = true; while (same && *c && *w) { unsigned char lc = *c++; if (lc >= 'A' && lc <= 'Z') lc += 'a'-'A'; if (lc != *w++) // different same = false; } if (same && *w == 0) // only the same if word has been exhausted return (const char *)s - str; // otherwise, find a space and skip to the end of the whitespace while (*s && *s != ' ') s++; while (*s && *s == ' ') s++; // and repeat until we're done } while (*s); return CStdString::npos; } // assumes it is called from after the first open bracket is found int StringUtils::FindEndBracket(const CStdString &str, char opener, char closer, int startPos) { int blocks = 1; for (unsigned int i = startPos; i < str.size(); i++) { if (str[i] == opener) blocks++; else if (str[i] == closer) { blocks--; if (!blocks) return i; } } return (int)CStdString::npos; } void StringUtils::WordToDigits(CStdString &word) { static const char word_to_letter[] = "22233344455566677778889999"; word.ToLower(); for (unsigned int i = 0; i < word.size(); ++i) { // NB: This assumes ascii, which probably needs extending at some point. char letter = word[i]; if ((letter >= 'a' && letter <= 'z')) // assume contiguous letter range { word[i] = word_to_letter[letter-'a']; } else if (letter < '0' || letter > '9') // We want to keep 0-9! { word[i] = ' '; // replace everything else with a space } } } float StringUtils::GetFloat(const char* str) { istringstream converter; converter.imbue(locale("C")); converter.str(str); float result; converter >> result; return result; }