2017-10-02 23:24:00 +02:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
2018-09-04 05:40:41 +02:00
using System.Runtime.CompilerServices ;
2017-10-02 23:24:00 +02:00
using System.Security ;
using System.Text ;
using System.Text.RegularExpressions ;
namespace Godot
{
public static class StringExtensions
{
2017-11-21 23:32:19 +01:00
private static int GetSliceCount ( this string instance , string splitter )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
if ( instance . Empty ( ) | | splitter . Empty ( ) )
2017-10-02 23:24:00 +02:00
return 0 ;
int pos = 0 ;
int slices = 1 ;
2017-11-21 23:32:19 +01:00
while ( ( pos = instance . Find ( splitter , pos ) ) > = 0 )
2017-10-02 23:24:00 +02:00
{
slices + + ;
pos + = splitter . Length ;
}
return slices ;
}
2018-09-10 21:22:04 +02:00
private static string GetSliceCharacter ( this string instance , char splitter , int slice )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
if ( ! instance . Empty ( ) & & slice > = 0 )
2017-10-02 23:24:00 +02:00
{
int i = 0 ;
int prev = 0 ;
int count = 0 ;
while ( true )
{
2018-09-10 21:22:04 +02:00
bool end = instance . Length < = i ;
if ( end | | instance [ i ] = = splitter )
2017-10-02 23:24:00 +02:00
{
if ( slice = = count )
{
return instance . Substring ( prev , i - prev ) ;
}
2018-09-10 21:22:04 +02:00
else if ( end )
{
return string . Empty ;
}
2018-04-08 05:28:24 +02:00
count + + ;
prev = i + 1 ;
2017-10-02 23:24:00 +02:00
}
i + + ;
}
}
return string . Empty ;
}
// <summary>
// If the string is a path to a file, return the path to the file without the extension.
// </summary>
2018-09-10 21:22:04 +02:00
public static string BaseName ( this string instance )
2017-10-02 23:24:00 +02:00
{
int index = instance . LastIndexOf ( '.' ) ;
if ( index > 0 )
return instance . Substring ( 0 , index ) ;
return instance ;
}
// <summary>
// Return true if the strings begins with the given string.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool BeginsWith ( this string instance , string text )
2017-10-02 23:24:00 +02:00
{
return instance . StartsWith ( text ) ;
}
// <summary>
// Return the bigrams (pairs of consecutive letters) of this string.
// </summary>
2017-11-21 23:32:19 +01:00
public static string [ ] Bigrams ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-04-08 05:30:43 +02:00
var b = new string [ instance . Length - 1 ] ;
2017-10-02 23:24:00 +02:00
2018-04-17 00:33:42 +02:00
for ( int i = 0 ; i < b . Length ; i + + )
2017-10-02 23:24:00 +02:00
{
b [ i ] = instance . Substring ( i , 2 ) ;
}
return b ;
}
2019-01-18 09:29:28 +01:00
// <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 ;
}
2017-10-02 23:24:00 +02:00
// <summary>
// Return a copy of the string with special characters escaped using the C language standard.
// </summary>
2017-11-21 23:32:19 +01:00
public static string CEscape ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-04-08 05:30:43 +02:00
var sb = new StringBuilder ( string . Copy ( instance ) ) ;
2017-10-02 23:24:00 +02:00
sb . Replace ( "\\" , "\\\\" ) ;
sb . Replace ( "\a" , "\\a" ) ;
sb . Replace ( "\b" , "\\b" ) ;
sb . Replace ( "\f" , "\\f" ) ;
sb . Replace ( "\n" , "\\n" ) ;
sb . Replace ( "\r" , "\\r" ) ;
sb . Replace ( "\t" , "\\t" ) ;
sb . Replace ( "\v" , "\\v" ) ;
sb . Replace ( "\'" , "\\'" ) ;
sb . Replace ( "\"" , "\\\"" ) ;
sb . Replace ( "?" , "\\?" ) ;
return sb . ToString ( ) ;
}
// <summary>
// Return a copy of the string with escaped characters replaced by their meanings according to the C language standard.
// </summary>
2017-11-21 23:32:19 +01:00
public static string CUnescape ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-04-08 05:30:43 +02:00
var sb = new StringBuilder ( string . Copy ( instance ) ) ;
2017-10-02 23:24:00 +02:00
sb . Replace ( "\\a" , "\a" ) ;
sb . Replace ( "\\b" , "\b" ) ;
sb . Replace ( "\\f" , "\f" ) ;
sb . Replace ( "\\n" , "\n" ) ;
sb . Replace ( "\\r" , "\r" ) ;
sb . Replace ( "\\t" , "\t" ) ;
sb . Replace ( "\\v" , "\v" ) ;
sb . Replace ( "\\'" , "\'" ) ;
sb . Replace ( "\\\"" , "\"" ) ;
sb . Replace ( "\\?" , "?" ) ;
sb . Replace ( "\\\\" , "\\" ) ;
return sb . ToString ( ) ;
}
// <summary>
// Change the case of some letters. Replace underscores with spaces, convert all letters to lowercase then capitalize first and every letter following the space character. For [code]capitalize camelCase mixed_with_underscores[/code] it will return [code]Capitalize Camelcase Mixed With Underscores[/code].
// </summary>
2017-11-21 23:32:19 +01:00
public static string Capitalize ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-04-17 00:53:27 +02:00
string aux = instance . Replace ( "_" , " " ) . ToLower ( ) ;
2018-04-08 05:30:43 +02:00
var cap = string . Empty ;
2017-10-02 23:24:00 +02:00
2018-04-17 00:33:42 +02:00
for ( int i = 0 ; i < aux . GetSliceCount ( " " ) ; i + + )
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
string slice = aux . GetSliceCharacter ( ' ' , i ) ;
2017-10-02 23:24:00 +02:00
if ( slice . Length > 0 )
{
slice = char . ToUpper ( slice [ 0 ] ) + slice . Substring ( 1 ) ;
if ( i > 0 )
cap + = " " ;
cap + = slice ;
}
}
return cap ;
}
// <summary>
// Perform a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
// </summary>
2017-11-21 23:32:19 +01:00
public static int CasecmpTo ( this string instance , string to )
2018-09-10 21:22:04 +02:00
{
return instance . CompareTo ( to , true ) ;
}
// <summary>
// Perform a comparison to another string, return -1 if less, 0 if equal and +1 if greater.
// </summary>
public static int CompareTo ( this string instance , string to , bool caseSensitive = true )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
if ( instance . Empty ( ) )
return to . Empty ( ) ? 0 : - 1 ;
2017-10-02 23:24:00 +02:00
2017-11-21 23:32:19 +01:00
if ( to . Empty ( ) )
2017-10-02 23:24:00 +02:00
return 1 ;
2018-09-10 21:22:04 +02:00
int instanceIndex = 0 ;
int toIndex = 0 ;
2018-11-20 11:14:07 +01:00
2018-09-10 21:22:04 +02:00
if ( caseSensitive ) // Outside while loop to avoid checking multiple times, despite some code duplication.
{
while ( true )
{
if ( to [ toIndex ] = = 0 & & instance [ instanceIndex ] = = 0 )
return 0 ; // We're equal
if ( instance [ instanceIndex ] = = 0 )
return - 1 ; // If this is empty, and the other one is not, then we're less... I think?
if ( to [ toIndex ] = = 0 )
return 1 ; // Otherwise the other one is smaller...
if ( instance [ instanceIndex ] < to [ toIndex ] ) // More than
return - 1 ;
if ( instance [ instanceIndex ] > to [ toIndex ] ) // Less than
return 1 ;
instanceIndex + + ;
toIndex + + ;
}
} else
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
while ( true )
{
if ( to [ toIndex ] = = 0 & & instance [ instanceIndex ] = = 0 )
return 0 ; // We're equal
if ( instance [ instanceIndex ] = = 0 )
return - 1 ; // If this is empty, and the other one is not, then we're less... I think?
if ( to [ toIndex ] = = 0 )
return 1 ; // Otherwise the other one is smaller..
if ( char . ToUpper ( instance [ instanceIndex ] ) < char . ToUpper ( to [ toIndex ] ) ) // More than
return - 1 ;
if ( char . ToUpper ( instance [ instanceIndex ] ) > char . ToUpper ( to [ toIndex ] ) ) // Less than
return 1 ;
instanceIndex + + ;
toIndex + + ;
}
2017-10-02 23:24:00 +02:00
}
}
// <summary>
// Return true if the string is empty.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool Empty ( this string instance )
2017-10-02 23:24:00 +02:00
{
return string . IsNullOrEmpty ( instance ) ;
}
// <summary>
// Return true if the strings ends with the given string.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool EndsWith ( this string instance , string text )
2017-10-02 23:24:00 +02:00
{
return instance . EndsWith ( text ) ;
}
// <summary>
// Erase [code]chars[/code] characters from the string starting from [code]pos[/code].
// </summary>
2017-11-21 23:32:19 +01:00
public static void Erase ( this StringBuilder instance , int pos , int chars )
2017-10-02 23:24:00 +02:00
{
instance . Remove ( pos , chars ) ;
}
// <summary>
// If the string is a path to a file, return the extension.
// </summary>
2017-11-21 23:32:19 +01:00
public static string Extension ( this string instance )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
int pos = instance . FindLast ( "." ) ;
2017-10-02 23:24:00 +02:00
if ( pos < 0 )
return instance ;
2018-05-21 12:45:48 +02:00
return instance . Substring ( pos + 1 ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Find the first occurrence of a substring, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed.
// </summary>
2017-11-21 23:32:19 +01:00
public static int Find ( this string instance , string what , int from = 0 )
2017-10-02 23:24:00 +02:00
{
return instance . IndexOf ( what , StringComparison . OrdinalIgnoreCase ) ;
}
// <summary>
// Find the last occurrence of a substring, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed.
// </summary>
2017-11-21 23:32:19 +01:00
public static int FindLast ( this string instance , string what )
2017-10-02 23:24:00 +02:00
{
return instance . LastIndexOf ( what , StringComparison . OrdinalIgnoreCase ) ;
}
// <summary>
// Find the first occurrence of a substring but search as case-insensitive, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed.
// </summary>
2017-11-21 23:32:19 +01:00
public static int FindN ( this string instance , string what , int from = 0 )
2017-10-02 23:24:00 +02:00
{
return instance . IndexOf ( what , StringComparison . Ordinal ) ;
}
// <summary>
// If the string is a path to a file, return the base directory.
// </summary>
2017-11-21 23:32:19 +01:00
public static string GetBaseDir ( this string instance )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
int basepos = instance . Find ( "://" ) ;
2017-10-02 23:24:00 +02:00
2018-04-08 05:38:02 +02:00
string rs ;
2018-04-08 05:30:43 +02:00
var @base = string . Empty ;
2017-10-02 23:24:00 +02:00
if ( basepos ! = - 1 )
{
2018-04-08 05:30:43 +02:00
var end = basepos + 3 ;
2019-07-03 09:44:53 +02:00
rs = instance . Substring ( end ) ;
2017-10-02 23:24:00 +02:00
@base = instance . Substring ( 0 , end ) ;
}
else
{
2017-11-21 23:32:19 +01:00
if ( instance . BeginsWith ( "/" ) )
2017-10-02 23:24:00 +02:00
{
2019-07-03 09:44:53 +02:00
rs = instance . Substring ( 1 ) ;
2017-10-02 23:24:00 +02:00
@base = "/" ;
}
else
{
rs = instance ;
}
}
2017-11-21 23:32:19 +01:00
int sep = Mathf . Max ( rs . FindLast ( "/" ) , rs . FindLast ( "\\" ) ) ;
2017-10-02 23:24:00 +02:00
if ( sep = = - 1 )
return @base ;
2018-03-03 18:30:53 +01:00
return @base + rs . Substr ( 0 , sep ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// If the string is a path to a file, return the file and ignore the base directory.
// </summary>
2017-11-21 23:32:19 +01:00
public static string GetFile ( this string instance )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
int sep = Mathf . Max ( instance . FindLast ( "/" ) , instance . FindLast ( "\\" ) ) ;
2017-10-02 23:24:00 +02:00
if ( sep = = - 1 )
return instance ;
2019-07-03 09:44:53 +02:00
return instance . Substring ( sep + 1 ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Hash the string and return a 32 bits integer.
// </summary>
2017-11-21 23:32:19 +01:00
public static int Hash ( this string instance )
2017-10-02 23:24:00 +02:00
{
int index = 0 ;
int hashv = 5381 ;
int c ;
2018-04-08 05:28:24 +02:00
while ( ( c = instance [ index + + ] ) ! = 0 )
2018-04-08 05:39:35 +02:00
hashv = ( hashv < < 5 ) + hashv + c ; // hash * 33 + c
2017-10-02 23:24:00 +02:00
return hashv ;
}
// <summary>
// Convert a string containing an hexadecimal number into an int.
// </summary>
2017-11-21 23:32:19 +01:00
public static int HexToInt ( this string instance )
2017-10-02 23:24:00 +02:00
{
int sign = 1 ;
if ( instance [ 0 ] = = '-' )
{
sign = - 1 ;
instance = instance . Substring ( 1 ) ;
}
if ( ! instance . StartsWith ( "0x" ) )
return 0 ;
return sign * int . Parse ( instance . Substring ( 2 ) , NumberStyles . HexNumber ) ;
}
// <summary>
// Insert a substring at a given position.
// </summary>
2017-11-21 23:32:19 +01:00
public static string Insert ( this string instance , int pos , string what )
2017-10-02 23:24:00 +02:00
{
return instance . Insert ( pos , what ) ;
}
// <summary>
// If the string is a path to a file or directory, return true if the path is absolute.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool IsAbsPath ( this string instance )
2017-10-02 23:24:00 +02:00
{
return System . IO . Path . IsPathRooted ( instance ) ;
}
// <summary>
// If the string is a path to a file or directory, return true if the path is relative.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool IsRelPath ( this string instance )
2017-10-02 23:24:00 +02:00
{
return ! System . IO . Path . IsPathRooted ( instance ) ;
}
// <summary>
// Check whether this string is a subsequence of the given string.
// </summary>
2018-09-10 21:22:04 +02:00
public static bool IsSubsequenceOf ( this string instance , string text , bool caseSensitive = true )
2017-10-02 23:24:00 +02:00
{
int len = instance . Length ;
if ( len = = 0 )
return true ; // Technically an empty string is subsequence of any string
if ( len > text . Length )
return false ;
2018-09-10 21:22:04 +02:00
int source = 0 ;
int target = 0 ;
2017-10-02 23:24:00 +02:00
2018-09-10 21:22:04 +02:00
while ( instance [ source ] ! = 0 & & text [ target ] ! = 0 )
2017-10-02 23:24:00 +02:00
{
2018-04-08 05:38:02 +02:00
bool match ;
2017-10-02 23:24:00 +02:00
2018-09-10 21:22:04 +02:00
if ( ! caseSensitive )
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
char sourcec = char . ToLower ( instance [ source ] ) ;
char targetc = char . ToLower ( text [ target ] ) ;
match = sourcec = = targetc ;
2017-10-02 23:24:00 +02:00
}
else
{
2018-09-10 21:22:04 +02:00
match = instance [ source ] = = text [ target ] ;
2017-10-02 23:24:00 +02:00
}
if ( match )
{
2018-09-10 21:22:04 +02:00
source + + ;
if ( instance [ source ] = = 0 )
2017-10-02 23:24:00 +02:00
return true ;
}
2018-09-10 21:22:04 +02:00
target + + ;
2017-10-02 23:24:00 +02:00
}
return false ;
}
// <summary>
2018-09-10 21:22:04 +02:00
// Check whether this string is a subsequence of the given string, ignoring case differences.
2017-10-02 23:24:00 +02:00
// </summary>
2017-11-21 23:32:19 +01:00
public static bool IsSubsequenceOfI ( this string instance , string text )
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
return instance . IsSubsequenceOf ( text , false ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Check whether the string contains a valid float.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool IsValidFloat ( this string instance )
2017-10-02 23:24:00 +02:00
{
float f ;
return float . TryParse ( instance , out f ) ;
}
// <summary>
// Check whether the string contains a valid color in HTML notation.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool IsValidHtmlColor ( this string instance )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
return Color . HtmlIsValid ( instance ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Check whether the string is a valid identifier. As is common in programming languages, a valid identifier may contain only letters, digits and underscores (_) and the first character may not be a digit.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool IsValidIdentifier ( this string instance )
2017-10-02 23:24:00 +02:00
{
int len = instance . Length ;
if ( len = = 0 )
return false ;
2018-04-17 00:33:42 +02:00
for ( int i = 0 ; i < len ; i + + )
2017-10-02 23:24:00 +02:00
{
if ( i = = 0 )
{
if ( instance [ 0 ] > = '0' & & instance [ 0 ] < = '9' )
return false ; // Don't start with number plz
}
2018-11-20 11:14:07 +01:00
bool validChar = instance [ i ] > = '0' & &
instance [ i ] < = '9' | | instance [ i ] > = 'a' & &
instance [ i ] < = 'z' | | instance [ i ] > = 'A' & &
2018-04-08 05:39:35 +02:00
instance [ i ] < = 'Z' | | instance [ i ] = = '_' ;
2017-10-02 23:24:00 +02:00
2018-09-10 21:22:04 +02:00
if ( ! validChar )
2017-10-02 23:24:00 +02:00
return false ;
}
return true ;
}
// <summary>
// Check whether the string contains a valid integer.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool IsValidInteger ( this string instance )
2017-10-02 23:24:00 +02:00
{
int f ;
return int . TryParse ( instance , out f ) ;
}
// <summary>
// Check whether the string contains a valid IP address.
// </summary>
2018-09-10 21:22:04 +02:00
public static bool IsValidIPAddress ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
// TODO: Support IPv6 addresses
2018-03-03 18:30:53 +01:00
string [ ] ip = instance . Split ( "." ) ;
2017-10-02 23:24:00 +02:00
if ( ip . Length ! = 4 )
return false ;
2018-04-17 00:33:42 +02:00
for ( int i = 0 ; i < ip . Length ; i + + )
2017-10-02 23:24:00 +02:00
{
string n = ip [ i ] ;
2017-11-21 23:32:19 +01:00
if ( ! n . IsValidInteger ( ) )
2017-10-02 23:24:00 +02:00
return false ;
2018-03-03 18:30:53 +01:00
int val = n . ToInt ( ) ;
2017-10-02 23:24:00 +02:00
if ( val < 0 | | val > 255 )
return false ;
}
return true ;
}
// <summary>
// Return a copy of the string with special characters escaped using the JSON standard.
// </summary>
2018-09-10 21:22:04 +02:00
public static string JSONEscape ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-04-08 05:30:43 +02:00
var sb = new StringBuilder ( string . Copy ( instance ) ) ;
2017-10-02 23:24:00 +02:00
sb . Replace ( "\\" , "\\\\" ) ;
sb . Replace ( "\b" , "\\b" ) ;
sb . Replace ( "\f" , "\\f" ) ;
sb . Replace ( "\n" , "\\n" ) ;
sb . Replace ( "\r" , "\\r" ) ;
sb . Replace ( "\t" , "\\t" ) ;
sb . Replace ( "\v" , "\\v" ) ;
sb . Replace ( "\"" , "\\\"" ) ;
return sb . ToString ( ) ;
}
// <summary>
// Return an amount of characters from the left of the string.
// </summary>
2017-11-21 23:32:19 +01:00
public static string Left ( this string instance , int pos )
2017-10-02 23:24:00 +02:00
{
if ( pos < = 0 )
return string . Empty ;
if ( pos > = instance . Length )
return instance ;
return instance . Substring ( 0 , pos ) ;
}
/// <summary>
/// Return the length of the string in characters.
/// </summary>
2017-11-21 23:32:19 +01:00
public static int Length ( this string instance )
2017-10-02 23:24:00 +02:00
{
return instance . Length ;
}
// <summary>
// Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'.
// </summary>
2017-11-21 23:32:19 +01:00
public static bool ExprMatch ( this string instance , string expr , bool caseSensitive )
2017-10-02 23:24:00 +02:00
{
if ( expr . Length = = 0 | | instance . Length = = 0 )
return false ;
switch ( expr [ 0 ] )
{
case '\0' :
return instance [ 0 ] = = 0 ;
case '*' :
2018-04-08 05:39:35 +02:00
return ExprMatch ( expr + 1 , instance , caseSensitive ) | | instance [ 0 ] ! = 0 & & ExprMatch ( expr , instance + 1 , caseSensitive ) ;
2017-10-02 23:24:00 +02:00
case '?' :
2017-11-21 23:32:19 +01:00
return instance [ 0 ] ! = 0 & & instance [ 0 ] ! = '.' & & ExprMatch ( expr + 1 , instance + 1 , caseSensitive ) ;
2017-10-02 23:24:00 +02:00
default :
2017-11-21 23:32:19 +01:00
return ( caseSensitive ? instance [ 0 ] = = expr [ 0 ] : char . ToUpper ( instance [ 0 ] ) = = char . ToUpper ( expr [ 0 ] ) ) & &
ExprMatch ( expr + 1 , instance + 1 , caseSensitive ) ;
2017-10-02 23:24:00 +02:00
}
}
// <summary>
// Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]).
// </summary>
2018-09-10 21:22:04 +02:00
public static bool Match ( this string instance , string expr , bool caseSensitive = true )
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
return instance . ExprMatch ( expr , caseSensitive ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
// </summary>
2018-09-10 21:22:04 +02:00
public static bool MatchN ( this string instance , string expr )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
return instance . ExprMatch ( expr , false ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Return the MD5 hash of the string as an array of bytes.
// </summary>
2018-09-10 21:22:04 +02:00
public static byte [ ] MD5Buffer ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-09-04 05:40:41 +02:00
return godot_icall_String_md5_buffer ( instance ) ;
2017-10-02 23:24:00 +02:00
}
2018-09-04 05:40:41 +02:00
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static byte [ ] godot_icall_String_md5_buffer ( string str ) ;
2017-10-02 23:24:00 +02:00
// <summary>
// Return the MD5 hash of the string as a string.
// </summary>
2018-09-10 21:22:04 +02:00
public static string MD5Text ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-09-04 05:40:41 +02:00
return godot_icall_String_md5_text ( instance ) ;
2017-10-02 23:24:00 +02:00
}
2018-09-04 05:40:41 +02:00
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_String_md5_text ( string str ) ;
2017-10-02 23:24:00 +02:00
// <summary>
// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
// </summary>
2017-11-21 23:32:19 +01:00
public static int NocasecmpTo ( this string instance , string to )
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
return instance . CompareTo ( to , false ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Return the character code at position [code]at[/code].
// </summary>
2017-11-21 23:32:19 +01:00
public static int OrdAt ( this string instance , int at )
2017-10-02 23:24:00 +02:00
{
return instance [ at ] ;
}
// <summary>
// Format a number to have an exact number of [code]digits[/code] after the decimal point.
// </summary>
2017-11-21 23:32:19 +01:00
public static string PadDecimals ( this string instance , int digits )
2017-10-02 23:24:00 +02:00
{
2017-11-21 23:32:19 +01:00
int c = instance . Find ( "." ) ;
2017-10-02 23:24:00 +02:00
if ( c = = - 1 )
{
if ( digits < = 0 )
return instance ;
instance + = "." ;
c = instance . Length - 1 ;
}
else
{
if ( digits < = 0 )
return instance . Substring ( 0 , c ) ;
}
if ( instance . Length - ( c + 1 ) > digits )
{
instance = instance . Substring ( 0 , c + digits + 1 ) ;
}
else
{
while ( instance . Length - ( c + 1 ) < digits )
{
instance + = "0" ;
}
}
return instance ;
}
// <summary>
// Format a number to have an exact number of [code]digits[/code] before the decimal point.
// </summary>
2017-11-21 23:32:19 +01:00
public static string PadZeros ( this string instance , int digits )
2017-10-02 23:24:00 +02:00
{
string s = instance ;
2017-11-21 23:32:19 +01:00
int end = s . Find ( "." ) ;
2017-10-02 23:24:00 +02:00
if ( end = = - 1 )
end = s . Length ;
if ( end = = 0 )
return s ;
int begin = 0 ;
while ( begin < end & & ( s [ begin ] < '0' | | s [ begin ] > '9' ) )
{
begin + + ;
}
if ( begin > = end )
return s ;
while ( end - begin < digits )
{
s = s . Insert ( begin , "0" ) ;
end + + ;
}
return s ;
}
// <summary>
// Decode a percent-encoded string. See [method percent_encode].
// </summary>
2017-11-21 23:32:19 +01:00
public static string PercentDecode ( this string instance )
2017-10-02 23:24:00 +02:00
{
return Uri . UnescapeDataString ( instance ) ;
}
// <summary>
// Percent-encode a string. This is meant to encode parameters in a URL when sending a HTTP GET request and bodies of form-urlencoded POST request.
// </summary>
2017-11-21 23:32:19 +01:00
public static string PercentEncode ( this string instance )
2017-10-02 23:24:00 +02:00
{
return Uri . EscapeDataString ( instance ) ;
}
// <summary>
// If the string is a path, this concatenates [code]file[/code] at the end of the string as a subpath. E.g. [code]"this/is".plus_file("path") == "this/is/path"[/code].
// </summary>
2017-11-21 23:32:19 +01:00
public static string PlusFile ( this string instance , string file )
2017-10-02 23:24:00 +02:00
{
if ( instance . Length > 0 & & instance [ instance . Length - 1 ] = = '/' )
return instance + file ;
2018-04-08 05:28:24 +02:00
return instance + "/" + file ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Replace occurrences of a substring for different ones inside the string.
// </summary>
2017-11-21 23:32:19 +01:00
public static string Replace ( this string instance , string what , string forwhat )
2017-10-02 23:24:00 +02:00
{
return instance . Replace ( what , forwhat ) ;
}
// <summary>
// Replace occurrences of a substring for different ones inside the string, but search case-insensitive.
// </summary>
2018-09-10 21:22:04 +02:00
public static string ReplaceN ( this string instance , string what , string forwhat )
2017-10-02 23:24:00 +02:00
{
return Regex . Replace ( instance , what , forwhat , RegexOptions . IgnoreCase ) ;
}
// <summary>
// Perform a search for a substring, but start from the end of the string instead of the beginning.
// </summary>
2018-09-10 21:22:04 +02:00
public static int RFind ( this string instance , string what , int from = - 1 )
2017-10-02 23:24:00 +02:00
{
2018-09-04 05:40:41 +02:00
return godot_icall_String_rfind ( instance , what , from ) ;
2017-10-02 23:24:00 +02:00
}
2018-09-04 05:40:41 +02:00
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_String_rfind ( string str , string what , int from ) ;
2017-10-02 23:24:00 +02:00
// <summary>
// Perform a search for a substring, but start from the end of the string instead of the beginning. Also search case-insensitive.
// </summary>
2018-09-10 21:22:04 +02:00
public static int RFindN ( this string instance , string what , int from = - 1 )
2017-10-02 23:24:00 +02:00
{
2018-09-04 05:40:41 +02:00
return godot_icall_String_rfindn ( instance , what , from ) ;
2017-10-02 23:24:00 +02:00
}
2018-09-04 05:40:41 +02:00
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_String_rfindn ( string str , string what , int from ) ;
2017-10-02 23:24:00 +02:00
// <summary>
// Return the right side of the string from a given position.
// </summary>
2017-11-21 23:32:19 +01:00
public static string Right ( this string instance , int pos )
2017-10-02 23:24:00 +02:00
{
if ( pos > = instance . Length )
return instance ;
if ( pos < 0 )
return string . Empty ;
2018-04-08 05:39:35 +02:00
return instance . Substring ( pos , instance . Length - pos ) ;
2017-10-02 23:24:00 +02:00
}
2018-09-10 21:22:04 +02:00
public static byte [ ] SHA256Buffer ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-09-04 05:40:41 +02:00
return godot_icall_String_sha256_buffer ( instance ) ;
2017-10-02 23:24:00 +02:00
}
2018-09-04 05:40:41 +02:00
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static byte [ ] godot_icall_String_sha256_buffer ( string str ) ;
2017-10-02 23:24:00 +02:00
// <summary>
// Return the SHA-256 hash of the string as a string.
// </summary>
2018-09-10 21:22:04 +02:00
public static string SHA256Text ( this string instance )
2017-10-02 23:24:00 +02:00
{
2018-09-04 05:40:41 +02:00
return godot_icall_String_sha256_text ( instance ) ;
2017-10-02 23:24:00 +02:00
}
2018-09-04 05:40:41 +02:00
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_String_sha256_text ( string str ) ;
2017-10-02 23:24:00 +02:00
// <summary>
// Return the similarity index of the text compared to this string. 1 means totally similar and 0 means totally dissimilar.
// </summary>
2017-11-21 23:32:19 +01:00
public static float Similarity ( this string instance , string text )
2017-10-02 23:24:00 +02:00
{
if ( instance = = text )
{
// Equal strings are totally similar
return 1.0f ;
}
if ( instance . Length < 2 | | text . Length < 2 )
{
// No way to calculate similarity without a single bigram
return 0.0f ;
}
2018-09-10 21:22:04 +02:00
string [ ] sourceBigrams = instance . Bigrams ( ) ;
string [ ] targetBigrams = text . Bigrams ( ) ;
2017-10-02 23:24:00 +02:00
2018-09-10 21:22:04 +02:00
int sourceSize = sourceBigrams . Length ;
int targetSize = targetBigrams . Length ;
2017-10-02 23:24:00 +02:00
2018-09-10 21:22:04 +02:00
float sum = sourceSize + targetSize ;
2017-10-02 23:24:00 +02:00
float inter = 0 ;
2018-09-10 21:22:04 +02:00
for ( int i = 0 ; i < sourceSize ; i + + )
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
for ( int j = 0 ; j < targetSize ; j + + )
2017-10-02 23:24:00 +02:00
{
2018-09-10 21:22:04 +02:00
if ( sourceBigrams [ i ] = = targetBigrams [ j ] )
2017-10-02 23:24:00 +02:00
{
inter + + ;
break ;
}
}
}
2018-04-08 05:39:35 +02:00
return 2.0f * inter / sum ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
// </summary>
2018-09-10 21:22:04 +02:00
public static string [ ] Split ( this string instance , string divisor , bool allowEmpty = true )
2017-10-02 23:24:00 +02:00
{
2018-04-08 05:28:24 +02:00
return instance . Split ( new [ ] { divisor } , StringSplitOptions . RemoveEmptyEntries ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",".
// </summary>
2018-09-10 21:22:04 +02:00
public static float [ ] SplitFloats ( this string instance , string divisor , bool allowEmpty = true )
2017-10-02 23:24:00 +02:00
{
2018-04-08 05:30:43 +02:00
var ret = new List < float > ( ) ;
2018-04-17 00:53:27 +02:00
int from = 0 ;
int len = instance . Length ;
2017-10-02 23:24:00 +02:00
while ( true )
{
2017-11-21 23:32:19 +01:00
int end = instance . Find ( divisor , from ) ;
2017-10-02 23:24:00 +02:00
if ( end < 0 )
end = len ;
2018-09-10 21:22:04 +02:00
if ( allowEmpty | | end > from )
2017-10-02 23:24:00 +02:00
ret . Add ( float . Parse ( instance . Substring ( from ) ) ) ;
if ( end = = len )
break ;
from = end + divisor . Length ;
}
return ret . ToArray ( ) ;
}
2018-09-10 21:22:04 +02:00
private static readonly char [ ] _nonPrintable = {
2017-10-02 23:24:00 +02:00
( char ) 00 , ( char ) 01 , ( char ) 02 , ( char ) 03 , ( char ) 04 , ( char ) 05 ,
( char ) 06 , ( char ) 07 , ( char ) 08 , ( char ) 09 , ( char ) 10 , ( char ) 11 ,
( char ) 12 , ( char ) 13 , ( char ) 14 , ( char ) 15 , ( char ) 16 , ( char ) 17 ,
( char ) 18 , ( char ) 19 , ( char ) 20 , ( char ) 21 , ( char ) 22 , ( char ) 23 ,
( char ) 24 , ( char ) 25 , ( char ) 26 , ( char ) 27 , ( char ) 28 , ( char ) 29 ,
( char ) 30 , ( char ) 31 , ( char ) 32
} ;
// <summary>
// Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively.
// </summary>
2018-03-03 18:30:53 +01:00
public static string StripEdges ( this string instance , bool left = true , bool right = true )
2017-10-02 23:24:00 +02:00
{
if ( left )
{
if ( right )
2018-09-10 21:22:04 +02:00
return instance . Trim ( _nonPrintable ) ;
return instance . TrimStart ( _nonPrintable ) ;
2017-10-02 23:24:00 +02:00
}
2018-04-08 05:28:24 +02:00
2018-09-10 21:22:04 +02:00
return instance . TrimEnd ( _nonPrintable ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Return part of the string from the position [code]from[/code], with length [code]len[/code].
// </summary>
2018-03-03 18:30:53 +01:00
public static string Substr ( this string instance , int from , int len )
2017-10-02 23:24:00 +02:00
{
2019-07-03 09:44:53 +02:00
int max = instance . Length - from ;
return instance . Substring ( from , len > max ? max : len ) ;
2017-10-02 23:24:00 +02:00
}
// <summary>
// Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters.
// </summary>
2018-03-03 18:30:53 +01:00
public static byte [ ] ToAscii ( this string instance )
2017-10-02 23:24:00 +02:00
{
return Encoding . ASCII . GetBytes ( instance ) ;
}
// <summary>
// Convert a string, containing a decimal number, into a [code]float[/code].
// </summary>
2018-03-03 18:30:53 +01:00
public static float ToFloat ( this string instance )
2017-10-02 23:24:00 +02:00
{
return float . Parse ( instance ) ;
}
// <summary>
// Convert a string, containing an integer number, into an [code]int[/code].
// </summary>
2018-03-03 18:30:53 +01:00
public static int ToInt ( this string instance )
2017-10-02 23:24:00 +02:00
{
return int . Parse ( instance ) ;
}
// <summary>
// Return the string converted to lowercase.
// </summary>
2018-03-03 18:30:53 +01:00
public static string ToLower ( this string instance )
2017-10-02 23:24:00 +02:00
{
return instance . ToLower ( ) ;
}
// <summary>
// Return the string converted to uppercase.
// </summary>
2018-03-03 18:30:53 +01:00
public static string ToUpper ( this string instance )
2017-10-02 23:24:00 +02:00
{
return instance . ToUpper ( ) ;
}
// <summary>
// Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii().
// </summary>
2018-09-10 21:22:04 +02:00
public static byte [ ] ToUTF8 ( this string instance )
2017-10-02 23:24:00 +02:00
{
return Encoding . UTF8 . GetBytes ( instance ) ;
}
// <summary>
// Return a copy of the string with special characters escaped using the XML standard.
// </summary>
2018-09-10 21:22:04 +02:00
public static string XMLEscape ( this string instance )
2017-10-02 23:24:00 +02:00
{
return SecurityElement . Escape ( instance ) ;
}
// <summary>
// Return a copy of the string with escaped characters replaced by their meanings according to the XML standard.
// </summary>
2018-09-10 21:22:04 +02:00
public static string XMLUnescape ( this string instance )
2017-10-02 23:24:00 +02:00
{
return SecurityElement . FromString ( instance ) . Text ;
}
}
}