sources/mystring.cpp

changeset 1
4dd5bde4e777
child 5
146825d63b9a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/mystring.cpp	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,482 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <cstring>
+#include "main.h"
+#include "mystring.h"
+#include "format.h"
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::compare (const String& other) const -> int
+{
+	return m_string.compare (other.std_string());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::trim (int n) -> void
+{
+	if (n > 0)
+		m_string = mid (0, length() - n).std_string();
+	else
+		m_string = mid (n, -1).std_string();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::strip (const List<char>& unwanted) -> String
+{
+	String copy (m_string);
+
+	for (char c : unwanted)
+	{
+		for (int pos = 0; (pos = copy.find (String (c))) != -1;)
+			copy.remove (pos--);
+	}
+
+	return copy;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_uppercase() const -> String
+{
+	String newstr (m_string);
+
+	for (char& c : newstr)
+	{
+		if (c >= 'a' and c <= 'z')
+			c -= 'a' - 'A';
+	}
+
+	return newstr;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_lowercase() const -> String
+{
+	String newstr (m_string);
+
+	for (char& c : newstr)
+	{
+		if (c >= 'A' and c <= 'Z')
+			c += 'a' - 'A';
+	}
+
+	return newstr;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::split (char del) const -> StringList
+{
+	String delimstr;
+	delimstr += del;
+	return split (delimstr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::split (const String& del) const -> StringList
+{
+	StringList res;
+	int a = 0;
+	int b;
+
+	// Find all separators and store the text left to them.
+	while ((b = find (del, a)) != -1)
+	{
+		String sub = mid (a, b);
+
+		if (sub.length() > 0)
+			res << sub;
+
+		a = b + del.length();
+	}
+
+	// Add the string at the right of the last separator
+	if (a < (int) length())
+		res.append (mid (a, length()));
+
+	return res;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::replace (const char* a, const char* b) -> void
+{
+	long pos;
+
+	while ((pos = find (a)) != -1)
+		m_string = m_string.replace (pos, strlen (a), b);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::count (char needle) const -> int
+{
+	int needles = 0;
+
+	for (const char & c : m_string)
+		if (c == needle)
+			needles++;
+
+	return needles;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::mid (long a, long b) const -> String
+{
+	if (b == -1 or b > length())
+		b = length();
+
+	if (b == a)
+		return "";
+
+	if (b < a)
+		swap (a, b);
+
+	if (a == 0 and b == length())
+		return *this;
+
+	char* newstr = new char[b - a + 1];
+	strncpy (newstr, chars() + a, b - a);
+	newstr[b - a] = '\0';
+	String other (newstr);
+	delete[] newstr;
+	return other;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::word_position (int n) const -> int
+{
+	int count = 0;
+
+	for (int i = 0; i < length(); ++i)
+	{
+		if (m_string[i] != ' ')
+			continue;
+
+		if (++count < n)
+			continue;
+
+		return i;
+	}
+
+	return -1;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::find (const char* c, int a) const -> int
+{
+	int pos = m_string.find (c, a);
+
+	if (pos == int (std::string::npos))
+		return -1;
+
+	return pos;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::find_last (const char* c, int a) const -> int
+{
+	if (a == -1 or a >= length())
+		a = length() - 1;
+
+	for (; a > 0; a--)
+		if (m_string[a] == c[0] and strncmp (chars() + a, c, strlen (c)) == 0)
+			return a;
+
+	return -1;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_int (bool* ok, int base) const -> long
+{
+	errno = 0;
+	char* endptr;
+	long i = strtol (chars(), &endptr, base);
+
+	if (ok)
+		*ok = (errno == 0 and *endptr == '\0');
+
+	return i;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_float (bool* ok) const -> float
+{
+	errno = 0;
+	char* endptr;
+	float i = strtof (chars(), &endptr);
+
+	if (ok != nullptr)
+		*ok = (errno == 0 and *endptr == '\0');
+
+	return i;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_double (bool* ok) const -> double
+{
+	errno = 0;
+	char* endptr;
+	double i = strtod (chars(), &endptr);
+
+	if (ok != nullptr)
+		*ok = (errno == 0 and *endptr == '\0');
+
+	return i;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::operator+ (const String& data) const -> String
+{
+	String newString = *this;
+	newString.append (data);
+	return newString;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::operator+ (const char* data) const -> String
+{
+	String newstr = *this;
+	newstr.append (data);
+	return newstr;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::is_numeric() const -> bool
+{
+	bool gotDot = false;
+
+	for (const char & c : m_string)
+	{
+		// Allow leading hyphen for negatives
+		if (&c == &m_string[0] and c == '-')
+			continue;
+
+		// Check for decimal point
+		if (!gotDot and c == '.')
+		{
+			gotDot = true;
+			continue;
+		}
+
+		if (c >= '0' and c <= '9')
+			continue; // Digit
+
+		// If the above cases didn't catch this character, it was
+		// illegal and this is therefore not a number.
+		return false;
+	}
+
+	return true;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::ends_with (const String& other) -> bool
+{
+	if (length() < other.length())
+		return false;
+
+	const int ofs = length() - other.length();
+	return strncmp (chars() + ofs, other.chars(), other.length()) == 0;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::starts_with (const String& other) -> bool
+{
+	if (length() < other.length())
+		return false;
+
+	return strncmp (chars(), other.chars(), other.length()) == 0;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::sprintf (const char* fmtstr, ...) -> void
+{
+	char* buf;
+	int bufsize = 256;
+	va_list va;
+	va_start (va, fmtstr);
+
+	do
+		buf = new char[bufsize];
+	while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize);
+
+	va_end (va);
+	m_string = buf;
+	delete[] buf;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+StringList::join (const String& delim) -> String
+{
+	String result;
+
+	for (const String& it : container())
+	{
+		if (result.is_empty() == false)
+			result += delim;
+
+		result += it;
+	}
+
+	return result;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::mask_against (const String& pattern) const -> bool
+{
+	// Elevate to uppercase for case-insensitive matching
+	String pattern_upper = pattern.to_uppercase();
+	String this_upper = to_uppercase();
+	const char* maskstring = pattern_upper.chars();
+	const char* mptr = &maskstring[0];
+
+	for (const char* sptr = this_upper.chars(); *sptr != '\0'; sptr++)
+	{
+		if (*mptr == '?')
+		{
+			if (*(sptr + 1) == '\0')
+			{
+				// ? demands that there's a character here and there wasn't.
+				// Therefore, mask matching fails
+				return false;
+			}
+		}
+		else if (*mptr == '*')
+		{
+			char end = *(++mptr);
+
+			// If '*' is the final character of the message, all of the remaining
+			// string matches against the '*'. We don't need to bother checking
+			// the string any further.
+			if (end == '\0')
+				return true;
+
+			// Skip to the end character
+			while (*sptr != end and *sptr != '\0')
+				sptr++;
+
+			// String ended while the mask still had stuff
+			if (*sptr == '\0')
+				return false;
+		}
+		else if (*sptr != *mptr)
+			return false;
+
+		mptr++;
+	}
+
+	return true;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::from_number (int a) -> String
+{
+	char buf[32];
+	::sprintf (buf, "%d", a);
+	return String (buf);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::from_number (long a) -> String
+{
+	char buf[32];
+	::sprintf (buf, "%ld", a);
+	return String (buf);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::from_number (unsigned long a) -> String
+{
+	char buf[32];
+	::sprintf (buf, "%lu", a);
+	return String (buf);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::from_number (double a) -> String
+{
+	char buf[64];
+	::sprintf (buf, "%f", a);
+	return String (buf);
+}

mercurial