Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added String.count method #25090

Merged
merged 1 commit into from
Jul 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions core/ustring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Chaosus marked this conversation as resolved.
Show resolved Hide resolved
}

int String::countn(const String &p_string, int p_from, int p_to) const {
Chaosus marked this conversation as resolved.
Show resolved Hide resolved
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();
Expand Down
4 changes: 4 additions & 0 deletions core/ustring.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -279,6 +280,9 @@ class String {
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;
Expand Down
5 changes: 5 additions & 0 deletions core/variant_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down
28 changes: 28 additions & 0 deletions doc/classes/String.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
39 changes: 39 additions & 0 deletions main/tests/test_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down Expand Up @@ -1116,6 +1154,7 @@ TestFunc test_funcs[] = {
test_32,
test_33,
test_34,
test_35,
0

};
Expand Down
14 changes: 14 additions & 0 deletions modules/gdnative/gdnative/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
20 changes: 20 additions & 0 deletions modules/gdnative/gdnative_api.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
]
}
]
},
Expand Down
2 changes: 2 additions & 0 deletions modules/gdnative/include/gdnative/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
60 changes: 60 additions & 0 deletions modules/mono/glue/Managed/Files/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,66 @@ public static string[] Bigrams(this string instance)
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>
Expand Down