Regex or loop in loop - c#

I have a function: bool ContainsOneOf(string str1, string validchars) which get 2 strings and checks if one of the letters in one string is in the other string.
Here's the code:
public static bool ContainsOneOf(string str1, string validchars)
{
foreach (char ch in str1)
{
foreach (char ch2 in validchars)
{
if (ch == ch2)
{
return true;
}
}
}
return false;
}
Other way to write it by using regex:
public static bool ContainsOneOf(string str1, string validchars)
{
return Regex.IsMatch(str1, #"[" + validchars + "]");
}
Which way should I use? (efficient)

Regex.IsMatch has too much overhead, I would use Enumerable.Any:
static bool ContainsOneOf(string str1, string validchars)
{
return str1.Any(c => validChars.Contains(c));
}

I suggest using linq:
public static bool ContainsOneOf(string str1, string validchars)
{
return str1.Any(ch => validchars.Any(ch2 => ch == ch2));
}
Or
public static bool ContainsOneOf(string str1, string validchars)
{
return (from ch in str1 from ch2 in validchars where ch == ch2 select ch).Any();
}

Just a variation to the other answers: I would switch str1 and validChars in the check, and also change the type of validChars to IEnumerable as to make it more reusable. Also, making it an extension method makes sense.
static bool ContainsOneOf(this string str1, IEnumerable<char> validchars)
{
return validchars.Any(c => str1.Contains(c));
}

An alternative to the other answers posted so far:
public static bool ContainsOneOf(string str1, string validchars)
{
return str1.IndexOfAny(validchars.ToCharArray()) != -1;
}
More on String.IndexOfAny:
https://msdn.microsoft.com/en-us/library/system.string.indexofany(v=vs.110).aspx

Related

c# compare strings with a comparison function

I may be missing the obvious but how do I provide a compare function to string.Equals() ?
I need to test for string equality but allow the first letter to be of a different case, so StringComparison is not useful, but I can't see a way to provide my own function to string.Equals ?
var s1="John";
var s2="john";
if (string.Equals(s1, s2, ????)) console.Write("Equal!!");
The Equals method does not have an overload that takes a custom compare function, but you could write one as an extension method:
public static class Extensions
{
public static bool EqualsCaseExceptFirst(this string input, string other)
{
if (input == null) throw new NullReferenceException();
if (ReferenceEquals(input, other)) return true;
if (input.Length != other.Length) return false;
if (input.Length == 0) return true;
if (!input.Substring(0, 1).Equals(other.Substring(0, 1),
StringComparison.OrdinalIgnoreCase)) return false;
return input.Length == 1 || input.Substring(1).Equals(other.Substring(1));
}
}
A sample test might look like:
private static void Main()
{
var testStrings = new List<string>
{
"hello", "Hello", "HELLO", "hELLO"
};
var sample = "Hello";
foreach (var testString in testStrings)
{
var result = sample.EqualsCaseExceptFirst(testString);
Console.WriteLine($"'{sample}' == '{testString}' : {result}");
}
Console.WriteLine("----------");
sample = "HELLO";
foreach (var testString in testStrings)
{
var result = sample.EqualsCaseExceptFirst(testString);
Console.WriteLine($"'{sample}' == '{testString}' : {result}");
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
You mentioned in the comments that you want to create an IEqualityComparer class, so here's a sample that simply reuses this method:
class CustomComparer : IEqualityComparer<string>
{
public bool Equals(string first, string second)
{
return first?.EqualsCaseExceptFirst(second) ?? false;
}
public int GetHashCode(string obj)
{
return obj?.GetHashCode() ?? 0;
}
}
You could just roll your own, add pepper and salt to taste
public static bool MyStringEquals(this string str1, string str2, StringComparison comparison = StringComparison.CurrentCulture)
=> !(str1?.Length > 0) || !(str2?.Length > 0) || string.Equals(str1.Substring(1), str2.Substring(1),comparison);
Usage
var s1 = "asd";
var s2 = "bsd";
var result = s1.MyStringEquals(s2, StringComparison.Ordinal);
Obviously, you will want to write yourself a bunch of test cases, and work out if this is what you want

Turn List into String with vowels and 'and' at the end [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm trying to make a string from a list. However, I want it to be arranged in a way that makes sense, so instead of the string being "I have apple, banana, Papa's citrus.", I want it to be "I have an apple, a banana, and Papa's citrus.". (Strings being "apple", "banana", and "Papa's citrus".)
I'd rather not change the strings making up my list; The thing I'm working on is a mod that changes the list depending on what other mods are enabled, so adding 'and' to the last string of each group won't work very well.
In all, the code I want would turn a list into a string, adding 'a' in front of words beginning with consonants, 'an' in front of words with vowels, and neither in front of words with apostrophes.
Thanks!
It's not clear what your input looks like, but it might be something like this console application. However, English is not my first language, so I might be terribly wrong:
static void Main(string[] args)
{
List<string> strings = new List<string>
{
"apple",
"banana",
"Papa's citrus"
};
var lastNdx = strings.Count - 1;
var sentence = "I have " + String.Join(", ",
strings.Select((s, ndx) =>
{
var ls = s.ToLower();
string ret = "";
if ("aeiou".Contains(ls[0]))
ret = "an " + s;
else if (ls.Contains("\'"))
ret = s;
else ret = "a " + s;
if (ndx == lastNdx)
ret = "and " + ret;
return ret;
}).ToArray() );
Console.WriteLine(sentence);
}
This answer is for #Stilgar. It is somewhat complicated, so I made it a second answer.
Download the latest version of the CMU Pronunciation Dictionary files and store them in a folder. Set the path in the CMUDictExt class to use that folder:
public static class CMUDictExt {
const string cmuFolder = #"D:\";
static IEnumerable<string> CMUFiles = Directory.EnumerateFiles(cmuFolder, "cmudict-*");
static Regex cmudictName = new Regex(#"cmudict-(?:\d+(?:\.\d+)?[a-z]?)+\.?(.*)$", RegexOptions.Compiled);
static string CMUFile(string ext) => CMUFiles.First(f => cmudictName.Match(f).Groups[1].Value == ext);
static Dictionary<string, string> phones;
static Dictionary<string, string[]> pronunciations;
public static ILookup<string, string> SymbolWords;
static HashSet<string> exceptions;
static CMUDictExt() {
phones = File.ReadLines(CMUFile("phones"))
.Select(l => l.Split('\t'))
.ToDictionary(pa => pa[0], pa => pa[1]);
pronunciations = File.ReadLines(CMUFile(""))
.Where(l => !l.StartsWith(";;;"))
.Where(l => Char.IsLetter(l[0]))
.Select(l => l.Split(" ").ToArray())
.ToDictionary(wg => wg[0].ToLowerInvariant(), wg => wg[1].Split(' '));
SymbolWords = pronunciations.SelectMany(wp => wp.Value.Select(s => (Word: wp.Key, s)))
.ToLookup(wp => wp.s, wp => wp.Word);
exceptions = pronunciations.Where(wp => (wp.Key.StartsWithVowel() ^ wp.Value[0].Phone() == "vowel")).Select(wp => wp.Key).ToHashSet();
}
public static string Phone(this string aSymbol) => phones.GetValueOrDefault(aSymbol.UpTo(ch => Char.IsDigit(ch)), String.Empty);
static string[] emptyStringArray = new string[] {};
public static string[] Pronunciation(this string aWord) => pronunciations.GetValueOrDefault(aWord.ToLowerInvariant(), emptyStringArray);
public static bool HasPronunciation(this string aWord) => pronunciations.GetValueOrDefault(aWord.ToLowerInvariant(), null) != null;
static readonly HashSet<char> vowels = "aeiou".ToHashSet<char>();
public static bool StartsWithVowel(this string w) => vowels.Contains(w[0]);
public static bool BeginsWithVowelSound(this string aWord) => exceptions.Contains(aWord) ? !aWord.StartsWithVowel() : aWord.StartsWithVowel(); // guess if not found
}
Using similar extension methods as before:
public static class IEnumerableExt {
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);
public static IEnumerable<T> Drop<T>(this ICollection<T> src, int drop) => (drop < 0) ? src.Leave(-drop) : src.Skip(drop);
public static T MinBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector, Comparer<TKey> keyComparer) => src.Aggregate((a, b) => keyComparer.Compare(keySelector(a), keySelector(b)) < 0 ? a : b);
public static T MinBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector) => src.Aggregate((a, b) => Comparer<TKey>.Default.Compare(keySelector(a), keySelector(b)) < 0 ? a : b);
}
public static class StringExt {
public static string UpTo(this string s, Regex stopRE) {
var m = stopRE.Match(s);
if (m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string UpTo(this string s, Func<char, bool> testfn) {
var m = s.Select((ch, Index) => new { ch, Index, Success = testfn(ch) }).FirstOrDefault(cit => cit.Success);
if (m != null && m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string Join(this IEnumerable<string> strings, string sep) => String.Join(sep, strings.ToArray());
public static bool EndsWithOneOf(this string s, params string[] endings) => endings.Any(e => s.EndsWith(e));
public static IEnumerable<string> Split(this string s, params string[] seps) => s.Split(StringSplitOptions.None, seps);
public static IEnumerable<string> Split(this string s, StringSplitOptions so, params string[] seps) {
int pos = 0;
do {
var sepPos = seps.Select(sep => new { pos = s.IndexOf(sep, pos) < 0 ? s.Length : s.IndexOf(sep, pos), len = sep.Length }).MinBy(pl => pl.pos);
if (sepPos.pos > pos || so == StringSplitOptions.None)
yield return s.Substring(pos, sepPos.pos - pos);
pos = sepPos.pos + sepPos.len;
} while (pos <= s.Length);
}
public static string FirstWord(this string phrase) => phrase.UpTo(ch => Char.IsWhiteSpace(ch));
public static bool IsAllLetters(this string s) => s.All(ch => Char.IsLetter(ch)); // faster than Regex
}
public static class DictionaryExt {
public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
}
You can now create some custom extensions to handle parts of the problem:
public static class FruitExt {
public static bool IsPossessive(this string phrase) => phrase.FirstWord().EndsWithOneOf("'s", "'");
public static string WithIndefiniteArticle(this string phrase) => (phrase.FirstWord().BeginsWithVowelSound() ? "an " : "a ") + phrase;
public static string ArticleOrPossessive(this string phrase) => phrase.IsPossessive() ? phrase : phrase.WithIndefiniteArticle();
}
Now the answer is calculated the same as before, but it handles many English language exceptions properly before defaulting to checking for a vowel:
var ans = ("I have " + src.Drop(-1).Select(w => w.ArticleOrPossessive()).Join(", ") + " and " + src.Last().ArticleOrPossessive() + ".");
Sample output:
I have an apple, a banana, Papa's citrus, an honest judge, a highchair, a university and an understanding.
Using some convenient extension methods:
public static class IEnumerableExt {
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);
public static IEnumerable<T> Drop<T>(this ICollection<T> src, int drop) => (drop < 0) ? src.Leave(-drop) : src.Skip(drop);
}
public static class StringExt {
public static string UpTo(this string s, Regex stopRE) {
var m = stopRE.Match(s);
if (m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string Join(this IEnumerable<string> strings, string sep) => String.Join(sep, strings.ToArray());
public static bool EndsWithOneOf(this string s, params string[] endings) => endings.Any(e => s.EndsWith(e));
}
You can then create some custom extension methods to break the problem down:
public static class FruitExt {
public static readonly HashSet<char> consonants = "bcdfghjklmnpqrstvwxyz".ToHashSet();
public static bool BeginsWithConsonant(this string w) => consonants.Contains(w[0]);
public static bool IsPossessive(this string w) => w.UpTo(new Regex(#"\s")).EndsWithOneOf("'s", "'");
public static string WithIndefiniteArticle(this string w) => (w.BeginsWithConsonant() ? "a " : "an ") + w;
public static string ArticleOrPossessive(this string w) => w.IsPossessive() ? w : w.WithIndefiniteArticle();
}
Note that BeginsWithConsonant fails for those (four?) words that begin with Y as a vowel - if it bothers you, you could add exceptions to the method.
Now the answer is fairly straight forward LINQ:
var ans = ("I have "+src.Drop(-1).Select(w => w.ArticleOrPossessive()).Join(", ")+" and "+src.Last().ArticleOrPossessive()+".");
Drop and Last aren't particularly efficient in the general case, but since we know we are using a List, they are fine (and the data is going to be short regardless).

How to override general comparison for two strings in C#

I've a question like the link below but much more complicated:
Ignoring accented letters in string comparison
I have a dictionary and some values inside like:
{[Ministère de l'économie, 139]}
{[Ministère des finances, 114]}
and for inserting new elements into the dictionary, as the sames codes, I wrote this:
if (!dict.ContainsKey(str))
{ dict.Add(str, Convert.ToDouble(number)); }
But when I want to check the existence of this value :
{[Ministère de l'economie, 139]} it returns it doesn't exist.
How can I implement this response to my code?
string.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace);
On the other way, as the title of my question, how can I overwrite the general (or the main) string comparison method in my application?
So, combining with the linked answer for removal of accents and diacritics, you can make an IEqualityComparer<string> to supply to your dictionary:
public class IgnoreAccentsAndDiacriticsComparer:IEqualityComparer<string>
{
public bool Equals(string left, string right)
{
if(left == null && right == null){ return true; }
if(left == null || right == null){ return false; }
return string.Equals(RemoveDiacritics(left), RemoveDiacritics(right));
}
public int GetHashCode(string txt)
{
return RemoveDiacritics(txt).GetHashCode();
}
static string RemoveDiacritics(string text)
{
string formD = text.Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
foreach (char ch in formD)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(ch);
if (uc != UnicodeCategory.NonSpacingMark)
{
sb.Append(ch);
}
}
return sb.ToString().Normalize(NormalizationForm.FormC);
}
}
Now, construct your dictionary using an instance of this comparer:
var myDic = new Dictionary<string, int>(new IgnoreAccentsAndDiacriticsComparer())
and try adding something accented:
myDic["Aimée"] = 1;
...and read it back out, without the accent:
Console.WriteLine(myDic["Aimee"]); //ŵôõ!
Starting from .NET 4.6 there are some interesting overloads of CompareInfo, so that the first solution suggested in https://stackoverflow.com/a/368850/613130 is usable in an IEqualityComparer<string>:
public class StringComparerIgnoreDiacritics : IEqualityComparer<string>
{
public static readonly StringComparerIgnoreDiacritics CurrentCulture = new StringComparerIgnoreDiacritics(CultureInfo.CurrentCulture.CompareInfo);
public readonly CompareInfo CompareInfo;
public StringComparerIgnoreDiacritics(CompareInfo compareInfo)
{
CompareInfo = compareInfo;
}
#region IEqualityComparer<string> Members
public bool Equals(string x, string y)
{
return CompareInfo.Compare(x, y, CompareOptions.IgnoreNonSpace) == 0;
}
public int GetHashCode(string obj)
{
return CompareInfo.GetHashCode(obj, CompareOptions.IgnoreNonSpace);
}
#endregion
}
and then
var myDic = new Dictionary<string, int>(var myDic = new Dictionary<string, int>(new IgnoreAccentsAndDiacriticsComparer()).CurrentCulture);

Regex combine more than 1 function

How can I call 2 functions in my code for one string?
public static string ecleaner(string str)
{
return Regex.Replace(str, "[éèê]+", "e", RegexOptions.Compiled);
}
public static string acleaner(string str)
{
return Regex.Replace(str, "[áàâ]+", "a", RegexOptions.Compiled);
}
Now I want to check the word "Téèááést" ,after this it should look like Teaest .
You could use a MatchEvaluator delegate, like this:
public static string cleaner(string str)
{
return Regex.Replace(str, "(?<a>[áàâ]+)|(?<e>[éèê]+)", onMatch, RegexOptions.Compiled);
}
private static string onMatch(Match m)
{
if (m.Groups["a"].Success)
return "a";
if (m.Groups["e"].Success)
return "e";
return "";
}
Or alternatively:
public static string cleaner(string str)
{
var groups = new[] { "a", "e" };
return Regex.Replace(str, "(?<a>[áàâ]+)|(?<e>[éèê]+)", m => groups.First(g => m.Groups[g].Success), RegexOptions.Compiled);
}
Did you try this?
string str = "Téèááést";
str = ecleaner(str);
str = acleaner(str);
public static class StringExtensions
{
public static string ecleaner(this string str)
{
return Regex.Replace(str, "[éèê]+", "e", RegexOptions.Compiled);
}
public static string acleaner(this string str)
{
return Regex.Replace(str, "[áàâ]+", "a", RegexOptions.Compiled);
}
}
//...
var result = "Téèááést".ecleaner().acleaner();
You could also combine an extension method class with #p.s.w.g's answer, to make things even neater.

Check String of chars

I'm looking for an fast method-solutions of my Problem to check a String, if this contains an min one char. IF String contains like any one of character in alphabet then return true, else false.
public bool checkString(String s)
{
return true || false;
}
For Example:
"1232133432454355467" return false
"134324239846c" return true
Try:
using System;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
var r = CheckString("112");
Console.WriteLine(r); // false
r = CheckString("112a");
Console.WriteLine(r); // true
}
public static bool CheckString(String input)
{
return Regex.Match(input, #"[a-zA-Z]").Success;
// or, as #Vlad L suggested
//return Regex.IsMatch(input, #"[a-zA-Z]");
}
}
If you want to verify against the "All Letters" Unicode character class, use this one instead:
return Regex.IsMatch(input, #"\p{L}");
Reference: Supported Unicode General Categories
If I understood the question correctly... This returns true if the string contains at least one letter.
public bool checkString(String s)
{
return s.Any(x => Char.IsLetter(x));
}
Try this with ToCharArray():
public bool checkString(String s)
{
bool retValue = s.ToCharArray()
.Any(c => ((int)c > 64 && (int)c < 91) ||
((int)c > 96 && (int)c < 123));
return retValue
}
Just for sake of completion.
// Regex to check the value consists of letters
// with atleast 1 character
private static Regex reg = new Regex(#"[a-zA-Z]+");
public bool checkString(String s)
{
return reg.Match(s).Success;
}
What about this?
if (Regex.IsMatch(yourString, "[a-zA-Z]"))
{
}
static void Main(string[] args)
{
Console.WriteLine(checkString("137563475634c756"));
}
static public bool checkString(String s)
{
return Regex.IsMatch(s, "[a-zA-Z]");
}
It Returns True.

Categories