237 lines
10 KiB
C#
237 lines
10 KiB
C#
// --------------------------------------------------------------------------------------------------------------------
|
|
// <copyright file="StringExt.cs" company="VRMADA">
|
|
// Copyright (c) VRMADA, All rights reserved.
|
|
// </copyright>
|
|
// --------------------------------------------------------------------------------------------------------------------
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace UltimateXR.Extensions.System
|
|
{
|
|
/// <summary>
|
|
/// <see cref="string" /> extensions.
|
|
/// </summary>
|
|
public static class StringExt
|
|
{
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Gets the number of occurrences of a string in another string.
|
|
/// </summary>
|
|
/// <param name="self">The string where to perform the search</param>
|
|
/// <param name="key">The string to find</param>
|
|
/// <param name="caseSensitive">Whether the search should be case sensitive</param>
|
|
/// <returns>Number of occurrences of <paramref name="key" /> in the source string</returns>
|
|
public static int GetOccurrenceCount(this string self, string key, bool caseSensitive = true)
|
|
{
|
|
if (string.IsNullOrEmpty(self) || string.IsNullOrEmpty(key))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (caseSensitive)
|
|
{
|
|
return (self.Length - self.Replace(key, string.Empty).Length) / key.Length;
|
|
}
|
|
|
|
return (self.Length - self.ToLower().Replace(key.ToLower(), string.Empty).Length) / key.Length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the SHA-256 hash value of a string.
|
|
/// </summary>
|
|
/// <param name="self">String to get the SHA-256 hash value of</param>
|
|
/// <returns>SHA-256 hash value of the string</returns>
|
|
public static byte[] GetSha256(this string self)
|
|
{
|
|
using HashAlgorithm algorithm = SHA256.Create();
|
|
return algorithm.ComputeHash(Encoding.ASCII.GetBytes(self));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the MD5 hash value of a string.
|
|
/// </summary>
|
|
/// <param name="self">String to get the MD5 hash value of</param>
|
|
/// <returns>MD5 hash value of the string</returns>
|
|
public static byte[] GetMd5(this string self)
|
|
{
|
|
using HashAlgorithm algorithm = MD5.Create();
|
|
return algorithm.ComputeHash(Encoding.ASCII.GetBytes(self));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the double SHA-256 hash value of a string.
|
|
/// </summary>
|
|
/// <param name="self">String to get the double SHA-256 hash value of</param>
|
|
/// <returns>Double SHA-256 hash value of the string</returns>
|
|
public static string GetSha256x2(this string self)
|
|
{
|
|
byte[] sha256 = self.GetSha256();
|
|
return sha256.Aggregate(new StringBuilder(sha256.Length * 2), (sb, b) => sb.AppendFormat("{0:x2}", b)).ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the double MD5 hash value of a string.
|
|
/// </summary>
|
|
/// <param name="self">String to get the double MD5 hash value of</param>
|
|
/// <returns>Double MD5 hash value of the string</returns>
|
|
public static string GetMd5x2(this string self)
|
|
{
|
|
byte[] md5 = self.GetMd5();
|
|
return md5.Aggregate(new StringBuilder(md5.Length * 2), (sb, b) => sb.AppendFormat("{0:x2}", b)).ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a Guid result of hashing the string using SHA-256 and keeping the first 16 bytes.
|
|
/// </summary>
|
|
/// <param name="self">String to get the Guid hash value of</param>
|
|
/// <returns>Guid hash value of the string</returns>
|
|
public static Guid GetGuid(this string self)
|
|
{
|
|
// Take the first 16 bytes of the hash to create a Guid
|
|
byte[] guidBytes = new byte[16];
|
|
Buffer.BlockCopy(GetSha256(self), 0, guidBytes, 0, guidBytes.Length);
|
|
|
|
return new Guid(guidBytes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replaces the invalid characters in a path with a given character.
|
|
/// </summary>
|
|
/// <param name="self">The path to process</param>
|
|
/// <param name="fallbackChar">The valid character to use as replacement</param>
|
|
/// <param name="invalidChars">The invalid characters to replace</param>
|
|
/// <returns>New string with the replacements</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">The replacement character is part of the invalid characters</exception>
|
|
public static string ReplaceInvalidPathChars(this string self, char fallbackChar = PathFallbackChar, params char[] invalidChars)
|
|
{
|
|
if (invalidChars.Length == 0)
|
|
{
|
|
return self.ReplaceInvalidDirPathChars(fallbackChar);
|
|
}
|
|
|
|
if (invalidChars.Contains(fallbackChar))
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(fallbackChar), fallbackChar, "Fallback should be a valid character");
|
|
}
|
|
|
|
return string.Join(fallbackChar.ToString(), self.Split(invalidChars));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replaces the invalid characters in a directory path with a given character.
|
|
/// </summary>
|
|
/// <param name="self">The directory path to process</param>
|
|
/// <param name="fallbackChar">The valid character to use as replacement</param>
|
|
/// <returns>New string with the replacements</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">The replacement character is part of the invalid characters</exception>
|
|
public static string ReplaceInvalidDirPathChars(this string self, char fallbackChar = PathFallbackChar)
|
|
{
|
|
return self.ReplaceInvalidPathChars(fallbackChar, Path.GetInvalidPathChars());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replaces the invalid characters in a file path with a given character.
|
|
/// </summary>
|
|
/// <param name="self">The file path to process</param>
|
|
/// <param name="fallbackChar">The valid character to use as replacement</param>
|
|
/// <returns>New string with the replacements</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">The replacement character is part of the invalid characters</exception>
|
|
public static string ReplaceInvalidFilePathChars(this string self, char fallbackChar = PathFallbackChar)
|
|
{
|
|
return self.ReplaceInvalidPathChars(fallbackChar, Path.GetInvalidFileNameChars());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a random string.
|
|
/// </summary>
|
|
/// <param name="length">String length</param>
|
|
/// <param name="includeLetters">Include letters in the string?</param>
|
|
/// <param name="includeNumbers">Include numbers in the string?</param>
|
|
/// <returns>Random string with given length or <see cref="string.Empty" /> if no letters and number were specified</returns>
|
|
public static string RandomString(int length, bool includeLetters, bool includeNumbers)
|
|
{
|
|
const string lettersOnly = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
const string numbersOnly = "0123456789";
|
|
const string lettersAndNumbers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
|
|
if (includeLetters && !includeNumbers)
|
|
{
|
|
return new string(Enumerable.Repeat(lettersOnly, length).Select(s => s[Random.Range(0, s.Length)]).ToArray());
|
|
}
|
|
if (!includeLetters && includeNumbers)
|
|
{
|
|
return new string(Enumerable.Repeat(numbersOnly, length).Select(s => s[Random.Range(0, s.Length)]).ToArray());
|
|
}
|
|
if (includeLetters && includeNumbers)
|
|
{
|
|
return new string(Enumerable.Repeat(lettersAndNumbers, length).Select(s => s[Random.Range(0, s.Length)]).ToArray());
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Splits a string using CamelCase.
|
|
/// </summary>
|
|
/// <param name="self">Input string</param>
|
|
/// <returns>Output string with added spaces</returns>
|
|
public static string SplitCamelCase(this string self)
|
|
{
|
|
return Regex.Replace(self, "([A-Z])", " $1", RegexOptions.Compiled).Trim();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Throws an exception if the string is null or only contains whitespaces.
|
|
/// </summary>
|
|
/// <param name="self">The string to check</param>
|
|
/// <param name="paramName">The parameter name, used as argument for the exceptions</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="self" /> is <see langword="null" /></exception>
|
|
/// <exception cref="ArgumentException">Whitespace string is not allowed</exception>
|
|
public static void ThrowIfNullOrWhitespace(this string self, string paramName)
|
|
{
|
|
if (self is null)
|
|
{
|
|
throw new ArgumentNullException(paramName);
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(self))
|
|
{
|
|
throw new ArgumentOutOfRangeException(paramName, self, "Value cannot be whitespace");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Throws an exception if the string is null or empty.
|
|
/// </summary>
|
|
/// <param name="self">The string</param>
|
|
/// <param name="paramName">The parameter name, used as arguments for the exceptions</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="self" /> is <see langword="null" /></exception>
|
|
/// <exception cref="ArgumentException">Empty string is not allowed</exception>
|
|
public static void ThrowIfNullOrEmpty(this string self, string paramName)
|
|
{
|
|
if (self == null)
|
|
{
|
|
throw new ArgumentNullException(paramName);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(self))
|
|
{
|
|
throw new ArgumentException("Empty string is not allowed", paramName);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Types & Data
|
|
|
|
private const char PathFallbackChar = '_';
|
|
|
|
#endregion
|
|
}
|
|
} |