Browse Source

Added count method to String

Chaosus 6 years ago
parent
commit
080c0bb7fe

+ 45 - 0
core/ustring.cpp

@@ -2729,6 +2729,51 @@ bool String::is_quoted() const {
 	return is_enclosed_in("\"") || is_enclosed_in("'");
 }
 
+int String::_count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const {
+	if (p_string.empty()) {
+		return 0;
+	}
+	int len = length();
+	int slen = p_string.length();
+	if (len < slen) {
+		return 0;
+	}
+	String str;
+	if (p_from >= 0 && p_to >= 0) {
+		if (p_to == 0) {
+			p_to = len;
+		} else if (p_from >= p_to) {
+			return 0;
+		}
+		if (p_from == 0 && p_to == len) {
+			str = String();
+			str.copy_from_unchecked(&c_str()[0], len);
+		} else {
+			str = substr(p_from, p_to - p_from);
+		}
+	} else {
+		return 0;
+	}
+	int c = 0;
+	int idx = -1;
+	do {
+		idx = p_case_insensitive ? str.findn(p_string) : str.find(p_string);
+		if (idx != -1) {
+			str = str.substr(idx + slen, str.length() - slen);
+			++c;
+		}
+	} while (idx != -1);
+	return c;
+}
+
+int String::count(const String &p_string, int p_from, int p_to) const {
+	return _count(p_string, p_from, p_to, false);
+}
+
+int String::countn(const String &p_string, int p_from, int p_to) const {
+	return _count(p_string, p_from, p_to, true);
+}
+
 bool String::_base_is_subsequence_of(const String &p_string, bool case_insensitive) const {
 
 	int len = length();

+ 4 - 0
core/ustring.h

@@ -137,6 +137,7 @@ class String {
 	void copy_from(const CharType &p_char);
 	void copy_from_unchecked(const CharType *p_char, const int p_length);
 	bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const;
+	int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const;
 
 public:
 	enum {
@@ -279,6 +280,9 @@ public:
 	String to_upper() const;
 	String to_lower() const;
 
+	int count(const String &p_string, int p_from = 0, int p_to = 0) const;
+	int countn(const String &p_string, int p_from = 0, int p_to = 0) const;
+
 	String left(int p_pos) const;
 	String right(int p_pos) const;
 	String dedent() const;

+ 5 - 0
core/variant_call.cpp

@@ -237,6 +237,8 @@ struct _VariantCall {
 	VCALL_LOCALMEM1R(String, casecmp_to);
 	VCALL_LOCALMEM1R(String, nocasecmp_to);
 	VCALL_LOCALMEM0R(String, length);
+	VCALL_LOCALMEM3R(String, count);
+	VCALL_LOCALMEM3R(String, countn);
 	VCALL_LOCALMEM2R(String, substr);
 	VCALL_LOCALMEM2R(String, find);
 	VCALL_LOCALMEM1R(String, find_last);
@@ -1502,6 +1504,9 @@ void register_variant_methods() {
 
 	ADDFUNC2R(STRING, INT, String, find, STRING, "what", INT, "from", varray(0));
 
+	ADDFUNC3R(STRING, INT, String, count, STRING, "what", INT, "from", INT, "to", varray(0, 0));
+	ADDFUNC3R(STRING, INT, String, countn, STRING, "what", INT, "from", INT, "to", varray(0, 0));
+
 	ADDFUNC1R(STRING, INT, String, find_last, STRING, "what", varray());
 	ADDFUNC2R(STRING, INT, String, findn, STRING, "what", INT, "from", varray(0));
 	ADDFUNC2R(STRING, INT, String, rfind, STRING, "what", INT, "from", varray(-1));

+ 28 - 0
doc/classes/String.xml

@@ -272,6 +272,34 @@
 				Performs a case-sensitive comparison to another string. Returns [code]-1[/code] if less than, [code]+1[/code] if greater than, or [code]0[/code] if equal.
 			</description>
 		</method>
+		<method name="count">
+			<return type="int">
+			</return>
+			<argument index="0" name="what" type="String">
+			</argument>
+			<argument index="1" name="from" type="int" default="0">
+			</argument>
+			<argument index="2" name="to" type="int" default="0">
+			</argument>
+			</argument>
+			<description>
+				Returns the number of occurrences of substring [code]what[/code] between [code]from[/code] and [code]to[/code] positions. If [code]from[/code] and [code]to[/code] equals 0 the whole string will be used. If only [code]to[/code] equals 0 the remained substring will be used.
+			</description>
+		</method>
+		<method name="countn">
+			<return type="int">
+			</return>
+			<argument index="0" name="what" type="String">
+			</argument>
+			<argument index="1" name="from" type="int" default="0">
+			</argument>
+			<argument index="2" name="to" type="int" default="0">
+			</argument>
+			</argument>
+			<description>
+				Returns the number of occurrences of substring [code]what[/code] (ignoring case) between [code]from[/code] and [code]to[/code] positions. If [code]from[/code] and [code]to[/code] equals 0 the whole string will be used. If only [code]to[/code] equals 0 the remained substring will be used.
+			</description>
+		</method>
 		<method name="dedent">
 			<return type="String">
 			</return>

+ 39 - 0
main/tests/test_string.cpp

@@ -1078,6 +1078,44 @@ bool test_34() {
 	return state;
 }
 
+bool test_35() {
+#define COUNT_TEST(x)                                            \
+	{                                                            \
+		bool success = x;                                        \
+		state = state && success;                                \
+		if (!success) {                                          \
+			OS::get_singleton()->print("\tfailed at: %s\n", #x); \
+		}                                                        \
+	}
+
+	OS::get_singleton()->print("\n\nTest 35: count and countn function\n");
+	bool state = true;
+
+	COUNT_TEST(String("").count("Test") == 0);
+	COUNT_TEST(String("Test").count("") == 0);
+	COUNT_TEST(String("Test").count("test") == 0);
+	COUNT_TEST(String("Test").count("TEST") == 0);
+	COUNT_TEST(String("TEST").count("TEST") == 1);
+	COUNT_TEST(String("Test").count("Test") == 1);
+	COUNT_TEST(String("aTest").count("Test") == 1);
+	COUNT_TEST(String("Testa").count("Test") == 1);
+	COUNT_TEST(String("TestTestTest").count("Test") == 3);
+	COUNT_TEST(String("TestTestTest").count("TestTest") == 1);
+	COUNT_TEST(String("TestGodotTestGodotTestGodot").count("Test") == 3);
+
+	COUNT_TEST(String("TestTestTestTest").count("Test", 4, 8) == 1);
+	COUNT_TEST(String("TestTestTestTest").count("Test", 4, 12) == 2);
+	COUNT_TEST(String("TestTestTestTest").count("Test", 4, 16) == 3);
+	COUNT_TEST(String("TestTestTestTest").count("Test", 4) == 3);
+
+	COUNT_TEST(String("Test").countn("test") == 1);
+	COUNT_TEST(String("Test").countn("TEST") == 1);
+	COUNT_TEST(String("testTest-Testatest").countn("tEst") == 4);
+	COUNT_TEST(String("testTest-TeStatest").countn("tEsT", 4, 16) == 2);
+
+	return state;
+}
+
 typedef bool (*TestFunc)(void);
 
 TestFunc test_funcs[] = {
@@ -1116,6 +1154,7 @@ TestFunc test_funcs[] = {
 	test_32,
 	test_33,
 	test_34,
+	test_35,
 	0
 
 };

+ 14 - 0
modules/gdnative/gdnative/string.cpp

@@ -186,6 +186,20 @@ godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_
 	return self->ends_with(*string);
 }
 
+godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) {
+	const String *self = (const String *)p_self;
+	String *what = (String *)&p_what;
+
+	return self->count(*what, p_from, p_to);
+}
+
+godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) {
+	const String *self = (const String *)p_self;
+	String *what = (String *)&p_what;
+
+	return self->countn(*what, p_from, p_to);
+}
+
 godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what) {
 	const String *self = (const String *)p_self;
 	String *what = (String *)&p_what;

+ 20 - 0
modules/gdnative/gdnative_api.json

@@ -44,6 +44,26 @@
               ["const godot_vector2 *", "p_to"],
               ["const godot_real", "p_delta"]
             ]
+          },       
+          {
+            "name": "godot_string_count",
+            "return_type": "godot_int",
+            "arguments": [
+              ["const godot_string *", "p_self"],
+              ["godot_string", "p_what"],
+              ["godot_int", "p_from"],
+              ["godot_int", "p_to"]
+            ]
+          }, 
+          {
+            "name": "godot_string_countn",
+            "return_type": "godot_int",
+            "arguments": [
+              ["const godot_string *", "p_self"],
+              ["godot_string", "p_what"],
+              ["godot_int", "p_from"],
+              ["godot_int", "p_to"]
+            ]
           }
         ]
       },

+ 2 - 0
modules/gdnative/include/gdnative/string.h

@@ -102,6 +102,8 @@ godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self,
 godot_array GDAPI godot_string_bigrams(const godot_string *p_self);
 godot_string GDAPI godot_string_chr(wchar_t p_character);
 godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_string *p_string);
+godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to);
+godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to);
 godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what);
 godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
 godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys);

+ 60 - 0
modules/mono/glue/Managed/Files/StringExtensions.cs

@@ -97,6 +97,66 @@ namespace Godot
             return b;
         }
 
+        // <summary>
+        // Return the amount of substrings in string.
+        // </summary>
+        public static int Count(this string instance, string what, bool caseSensitive = true, int from = 0, int to = 0)
+        {
+            if (what.Length == 0)
+            {
+                return 0;
+            }
+
+            int len = instance.Length;
+            int slen = what.Length;
+
+            if (len < slen)
+            {
+                return 0;
+            }
+
+            string str;
+
+            if (from >= 0 && to >= 0)
+            {
+                if (to == 0)
+                {
+                    to = len;
+                }
+                else if (from >= to)
+                {
+                    return 0;
+                }
+                if (from == 0 && to == len)
+                {
+                    str = instance;
+                }
+                else
+                {
+                    str = instance.Substring(from, to - from);
+                }
+            }
+            else
+            {
+                return 0;
+            }
+
+            int c = 0;
+            int idx;
+
+            do
+            {
+                idx = str.IndexOf(what, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+                if (idx != -1)
+                {
+                    str = str.Substring(idx + slen);
+                    ++c;
+                }
+            } while (idx != -1);
+
+            return c;
+        }
+
         // <summary>
         // Return a copy of the string with special characters escaped using the C language standard.
         // </summary>