C# - Compare two strings by using dots in the strings and using wildcards: * - c#

I have two string variables that i want to compare.
var compareA = "something.somethingelse.another.something2"
var compareB = "*.another.something2"
I want to compare this, and the result is: True.
var compareC = "something.somethingelse.*"
compared to compareA, the result should also be: True.
Of course, the fact that both variables can contain N dots also complicates the task.
How would you start for him?
I was tried this:
static void Main(string[] args)
{
var A = CompareString("*.something", "other.Another.something"); //I need this is true!
var B = CompareString("something.Value.Other.*", "something.Value.Other.SomethingElse"); //I need this is true
var C = CompareString("something.Value.Other", "something.Value.Other.OtherElse"); //I need this is False
var D = CompareString("*.somethingElse", "other.another.Value"); //I Need this is false
Console.WriteLine("It is need True: {0}", A);
Console.WriteLine("It is need True: {0}", B);
Console.WriteLine("It is need False: {0}", C);
Console.WriteLine("It is need False: {0}", D);
}
private static bool CompareString(string first, string second)
{
var resume = false;
var firstSplit = first.Split('.');
var secondSplit = second.Split('.');
foreach (var firstItem in firstSplit)
{
foreach (var secondItem in secondSplit)
{
if (firstItem == "*" || secondItem == "*" || string.Equals(firstItem.ToLower(), secondItem.ToLower()))
{
resume = true;
}
else
{
resume = false;
}
}
}
return resume;
}
The results are good, but I think it can be done differently, and the reasoning may be wrong.

Assuming the following:
Compare 1 string to another and it's the full string of 1 contained inside the longer of the 2 strings.
Case to be ignored as well as culture.
The full stops are considered part of the string, not actually as a separator.
Wildcards can be used to state how 1 string can be contained within another.
You should be able to use the following
private bool HasMatch(string textToSearch, string searchText)
{
if (textToSearch.Length < searchText.Length) return false;
var wildCardIndex = searchText.IndexOf('*');
if (wildCardIndex == -1)
{
return textToSearch.Equals(searchText, StringComparison.InvariantCultureIgnoreCase);
}
else
{
if (wildCardIndex == 0)
{
var text = searchText.TrimStart('*');
return textToSearch.EndsWith(text, StringComparison.InvariantCultureIgnoreCase);
}
if (wildCardIndex == (searchText.Length - 1))
{
var text = searchText.TrimEnd('*');
return textToSearch.StartsWith(text, StringComparison.InvariantCultureIgnoreCase);
}
}
return false;
}

Related

Looking to check for one's or zeros in a Bitstring

Does anyone have an idea how to check for one's or zeros in a Bitstring? The below code checks for ones and zeros in a string, but I would like to add an extension bitstring that does the same thing. This way, I can use the method on the bitstring itself with out having to first evaluate the string.
Currently, I have to check before I entered the bitstring method.
string MustBeBitsInStringOnesOrZeros = "11001";
bool boTesting = Is1Or0(MustBeBitsInStringOnesOrZeros);
// I would like to add an extension to check for ones and zeros
// Example: MustBeBitsInStringOnesOrZeros.Is1Or0();
if (boTesting == true)
{
Bitstring a = new Bitstring(MustBeBitsInStringOnesOrZeros);
}
else
{
string b = MustBeBitsInStringOnesOrZeros;
}
private static bool Is1Or0(string stringBit)
{
// This function check each
// character in a string for "1"
// or "0".
bool results = false;
for (int i = 0; i < stringBit.Length; i++)
{
var x = stringBit[i];
if (x == '1' || x == '0')
{
results = true;
}
else
{
results = false;
break;
}
}
return results;
}
===
Modified to show results of Bassie's example from a sealed class.
Bassie,
Well, what I was trying to say was that I cannot place the method in the sealed class with the keyword 'this' in the method. So I created another class but, I have to use it a different way and I wanted to use it the way you call it.
//I have to use it this way:
Bitstring OnesAndZeroCheck = new Bitstring(); // Bitstring is in a sealed class
Boolean g = OnesAndZeroCheck.IsBitstring2("1100111100011100101010101010101101010101010"); // Is in the sealed class
//but want to call it this way:
var successInput = "1101";
successInput.Is1Or0(); // true
If I understand you correctly, you could define your extension method like this
public static class StringExtensions
{
public static bool Is1Or0(this string stringBit)
=> stringBit.All(c => c == '1' || c == '0');
}
And call with
var successInput = "1101";
successInput.Is1Or0(); // true
var failureInput = "1121"
failureInput.Is1Or0(); // false
From MSDN Enumerable.All:
Determines whether all elements of a sequence satisfy a condition.
This works because a string is actually just an IEnumerable of char - so when we call the IEnumerable.All() extension method, we check the condition against each individual char in the string
Note you will need to include using System.Linq; to your file that contains the extension method
tested in video.
https://youtu.be/CgMFYctc3Ak
public static bool isBitstring(string s)
{
foreach (char c in s)
{
if (!(c >= '0' && c <= '1')) {
return false;
}
}
return true;
}
string str = "100000011100101010010101";
if (isBitstring(str))
{
Console.WriteLine("is Bitstring");
}
else
{
Console.WriteLine("is not Bitstring");
}

How to validate or compare string by omitting certain part of it

I have a string as below
"a1/type/xyz/parts"
The part where 'xyz' exists is dynamic and varies accordingly at any size. I want to compare just the two strings are equal discarding the 'xyz' portion exactly.
For example I have string as below
"a1/type/abcd/parts"
Then my comparison has to be successful
I tried with regular expression as below. Though my knowledge on regular expressions is limited and it did not work. Probably something wrong in the way I used.
var regex = #"^[a-zA-Z]{2}/\[a-zA-Z]{16}/\[0-9a-zA-Z]/\[a-z]{5}/$";
var result = Regex.Match("mystring", regex).Success;
Another idea is to get substring of first and last part omitting the unwanted portion and comparing it.
The comparison should be successful by discarding certain portion of the string with effective code.
Comparison successful cases
string1: "a1/type/21412ghh/parts"
string2: "a1/type/eeeee122ghh/parts"
Comparison failure cases:
string1: "a1/type/21412ghh/parts"
string2: "a2/type/eeeee122ghh/parts/mm"
In short "a1/type/abcd/parts" in this part of string the non-bold part is static always.
Honestly, you could do this using regex, and pull apart the string. But you have a specified delimiter, just use String.Split:
bool AreEqualAccordingToMyRules(string input1, string input2)
{
var split1 = input1.Split('/');
var split2 = input2.Split('/');
return split1.Length == split2.Length // strings must have equal number of sections
&& split1[0] == split2[0] // section 1 must match
&& split1[1] == split2[1] // section 2 must match
&& split1[3] == split2[3] // section 4 must match
}
You can try Split (to get parts) and Linq (to exclude 3d one)
using System.Linq;
...
string string1 = "a1/type/xyz/parts";
string string2 = "a1/type/abcd/parts";
bool result = string1
.Split('/') // string1 parts
.Where((v, i) => i != 2) // all except 3d one
.SequenceEqual(string2 // must be equal to
.Split('/') // string2 parts
.Where((v, i) => i != 2)); // except 3d one
Here's a small programm using string functions to compare the parts before and after the middle part:
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine(CutOutMiddle("a1/type/21412ghh/parts"));
Console.WriteLine("True: " + CompareNoMiddle("a1/type/21412ghh/parts", "a1/type/21412ghasdasdh/parts"));
Console.WriteLine("False: " + CompareNoMiddle("a1/type/21412ghh/parts", "a2/type/21412ghh/parts/someval"));
Console.WriteLine("False: " + CompareNoMiddle("a1/type/21412ghh/parts", "a1/type/21412ghasdasdh/parts/someappendix"));
}
private static bool CompareNoMiddle(string s1, string s2)
{
var s1CutOut = CutOutMiddle(s1);
var s2CutOut = CutOutMiddle(s2);
return s1CutOut == s2CutOut;
}
private static string CutOutMiddle(string val)
{
var fistSlash = val.IndexOf('/', 0);
var secondSlash = val.IndexOf('/', fistSlash+1);
var thirdSlash = val.IndexOf('/', secondSlash+1);
var firstPart = val.Substring(0, secondSlash);
var secondPart = val.Substring(thirdSlash, val.Length - thirdSlash);
return firstPart + secondPart;
}
}
returns
a1/type/parts
True: True
False: False
False: False
This solution should cover your case, as said by others, if you have a delimiter use it. In the function below you could change int skip for string ignore or something similar and within the comparison loop if(arrayStringOne[i] == ignore) continue;.
public bool Compare(string valueOne, string valueTwo, int skip) {
var delimiterOccuranceOne = valueOne.Count(f => f == '/');
var delimiterOccuranceTwo = valueTwo.Count(f => f == '/');
if(delimiterOccuranceOne == delimiterOccuranceTwo) {
var arrayStringOne = valueOne.Split('/');
var arrayStringTwo = valueTwo.Split('/');
for(int i=0; i < arrayStringOne.Length; ++i) {
if(i == skip) continue; // or instead of an index you could use a string
if(arrayStringOne[i] != arrayStringTwo[i]) {
return false;
}
}
return true;
}
return false;
}
Compare("a1/type/abcd/parts", "a1/type/xyz/parts", 2);

Check if string contains characters in certain order in C#r

I have a code that's working right now, but it doesn't check if the characters are in order, it only checks if they're there. How can I modify my code so the the characters 'gaoaf' are checked in that order in the string?
Console.WriteLine("5.feladat");
StreamWriter sw = new StreamWriter("keres.txt");
sw.WriteLine("gaoaf");
string s = "";
for (int i = 0; i < n; i++)
{
s = zadatok[i].nev+zadatok[i].cim;
if (s.Contains("g") && s.Contains("a") && s.Contains("o") && s.Contains("a") && s.Contains("f") )
{
sw.WriteLine(i);
sw.WriteLine(zadatok[i].nev + zadatok[i].cim);
}
}
sw.Close();
You can convert the letters into a pattern and use Regex:
var letters = "gaoaf";
var pattern = String.Join(".*",letters.AsEnumerable());
var hasletters = Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase);
For those that needlessly avoid .*, you can also solve this with LINQ:
var ans = letters.Aggregate(0, (p, c) => p >= 0 ? s.IndexOf(c.ToString(), p, StringComparison.InvariantCultureIgnoreCase) : p) != -1;
If it is possible to have repeated adjacent letters, you need to complicate the LINQ solution slightly:
var ans = letters.Aggregate(0, (p, c) => {
if (p >= 0) {
var newp = s.IndexOf(c.ToString(), p, StringComparison.InvariantCultureIgnoreCase);
return newp >= 0 ? newp+1 : newp;
}
else
return p;
}) != -1;
Given the (ugly) machinations required to basically terminate Aggregate early, and given the (ugly and inefficient) syntax required to use an inline anonymous expression call to get rid of the temporary newp, I created some extensions to help, an Aggregate that can terminate early:
public static TAccum AggregateWhile<TAccum, T>(this IEnumerable<T> src, TAccum seed, Func<TAccum, T, TAccum> accumFn, Predicate<TAccum> whileFn) {
using (var e = src.GetEnumerator()) {
if (!e.MoveNext())
throw new Exception("At least one element required by AggregateWhile");
var ans = accumFn(seed, e.Current);
while (whileFn(ans) && e.MoveNext())
ans = accumFn(ans, e.Current);
return ans;
}
}
Now you can solve the problem fairly easily:
var ans2 = letters.AggregateWhile(-1,
(p, c) => s.IndexOf(c.ToString(), p+1, StringComparison.InvariantCultureIgnoreCase),
p => p >= 0
) != -1;
Why not something like this?
static bool CheckInOrder(string source, string charsToCheck)
{
int index = -1;
foreach (var c in charsToCheck)
{
index = source.IndexOf(c, index + 1);
if (index == -1)
return false;
}
return true;
}
Then you can use the function like this:
bool result = CheckInOrder("this is my source string", "gaoaf");
This should work because IndexOf returns -1 if a string isn't found, and it only starts scanning AFTER the previous match.

Compare Two Liste <T>

how can i compare 2 list ?
public class Pers_Ordre : IEqualityComparer<Pers_Ordre>
{
int _ordreId;
public int LettreVoidID
{
get { return _LettreVoidID; }
set { _LettreVoidID = value; }
}
string _OrdreCummul;
public string OrdreCummul
{
get { return _OrdreCummul; }
set { _OrdreCummul = value; }
}
// Products are equal if their names and product numbers are equal.
public bool Equals(Pers_Ordre x, Pers_Ordre y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the products' properties are equal.
return x.LettreVoidID == y.LettreVoidID && x.OrdreCummul == y.OrdreCummul;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Pers_Ordre product)
{
//Check whether the object is null
if (Object.ReferenceEquals(product, null)) return 0;
//Get hash code for the Name field if it is not null.
int hashProductName = product.OrdreCummul == null ? 0 : product.OrdreCummul.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = product.LettreVoidID.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
}
and i compare like this:
private void simpleButton_Comparer_Click(object sender, EventArgs e)
{
string LeFile_Client = System.IO.Path.Combine(appDir, #"FA.csv");
string LeFile_Server = System.IO.Path.Combine(appDir, #"FA_Server.csv");
List<Pers_Ordre> oListClient = Outils.GetCsv(LeFile_Client).OrderBy(t => t.LettreVoidID).ToList();
List<Pers_Ordre> oListServert = Outils.GetCsvServer(LeFile_Server).OrderBy(t => t.LettreVoidID).ToList();
List<Pers_Ordre> LeDiff = new List<Pers_Ordre>();
LeDiff = oListServert.Except(oListClient).ToList();
string Noid = "", OdreID = "";
foreach (var oDiff in LeDiff)
{
Noid += oDiff.LettreVoidID + " ";
OdreID += oDiff.OrdreCummul + " ";
}
MessageBox.Show(Noid + "--" + OdreID);
}
i can not get the right result.
The Lists contain class objects and we would like to iterate through one list, looking for the same item in a second List and report any differences.
to get object that contains in List A but not in List B
and vice versa.
Your current .Except() call will find items from Server that are missing on the client, but it will not find items on the client that are missing on the server.
Try this:
private void simpleButton_Comparer_Click(object sender, EventArgs e)
{
string LeFile_Client = System.IO.Path.Combine(appDir, #"FA.csv");
string LeFile_Server = System.IO.Path.Combine(appDir, #"FA_Server.csv");
var ListClient = Outils.GetCsv(LeFile_Client).OrderBy(t => t.LettreVoidID);
var ListServer = Outils.GetCsvServer(LeFile_Server).OrderBy(t => t.LettreVoidID);
var LeDiff = ListServer.Except(ListClient).Concat(ListClient.Except(ListServer));
var result = new StringBuilder();
foreach (var Diff in LeDiff)
{
result.AppendFormat("{0} --{1} ", Diff.LettreVoidID, Diff.OrdreCummul);
}
MessageBox.Show(Noid.ToString() + "--" + OdreID);
}
This code should also be significantly faster than your original, as it avoids loading the results into memory until it builds the final string. This code in performs the equivalent of two separate sql LEFT JOINs. We could make it faster still by doing one FULL JOIN, but that would require writing our own linq operator method as well.

compare the characters in two strings

In C#, how do I compare the characters in two strings.
For example, let's say I have these two strings
"bc3231dsc" and "bc3462dsc"
How do I programically figure out the the strings
both start with "bc3" and end with "dsc"?
So the given would be two variables:
var1 = "bc3231dsc";
var2 = "bc3462dsc";
After comparing each characters from var1 to var2, I would want the output to be:
leftMatch = "bc3";
center1 = "231";
center2 = "462";
rightMatch = "dsc";
Conditions:
1. The strings will always be a length of 9 character.
2. The strings are not case sensitive.
The string class has 2 methods (StartsWith and Endwith) that you can use.
After reading your question and the already given answers i think there are some constraints are missing, which are maybe obvious to you, but not to the community. But maybe we can do a little guess work:
You'll have a bunch of string pairs that should be compared.
The two strings in each pair are of the same length or you are only interested by comparing the characters read simultaneously from left to right.
Get some kind of enumeration that tells me where each block starts and how long it is.
Due to the fact, that a string is only a enumeration of chars you could use LINQ here to get an idea of the matching characters like this:
private IEnumerable<bool> CommonChars(string first, string second)
{
if (first == null)
throw new ArgumentNullException("first");
if (second == null)
throw new ArgumentNullException("second");
var charsToCompare = first.Zip(second, (LeftChar, RightChar) => new { LeftChar, RightChar });
var matchingChars = charsToCompare.Select(pair => pair.LeftChar == pair.RightChar);
return matchingChars;
}
With this we can proceed and now find out how long each block of consecutive true and false flags are with this method:
private IEnumerable<Tuple<int, int>> Pack(IEnumerable<bool> source)
{
if (source == null)
throw new ArgumentNullException("source");
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
bool current = iterator.Current;
int index = 0;
int length = 1;
while (iterator.MoveNext())
{
if(current != iterator.Current)
{
yield return Tuple.Create(index, length);
index += length;
length = 0;
}
current = iterator.Current;
length++;
}
yield return Tuple.Create(index, length);
}
}
Currently i don't know if there is an already existing LINQ function that provides the same functionality. As far as i have already read it should be possible with SelectMany() (cause in theory you can accomplish any LINQ task with this method), but as an adhoc implementation the above was easier (for me).
These functions could then be used in a way something like this:
var firstString = "bc3231dsc";
var secondString = "bc3462dsc";
var commonChars = CommonChars(firstString, secondString);
var packs = Pack(commonChars);
foreach (var item in packs)
{
Console.WriteLine("Left side: " + firstString.Substring(item.Item1, item.Item2));
Console.WriteLine("Right side: " + secondString.Substring(item.Item1, item.Item2));
Console.WriteLine();
}
Which would you then give this output:
Left side: bc3
Right side: bc3
Left side: 231
Right side: 462
Left side: dsc
Right side: dsc
The biggest drawback is in someway the usage of Tuple cause it leads to the ugly property names Item1 and Item2 which are far away from being instantly readable. But if it is really wanted you could introduce your own simple class holding two integers and has some rock-solid property names. Also currently the information is lost about if each block is shared by both strings or if they are different. But once again it should be fairly simply to get this information also into the tuple or your own class.
static void Main(string[] args)
{
string test1 = "bc3231dsc";
string tes2 = "bc3462dsc";
string firstmatch = GetMatch(test1, tes2, false);
string lasttmatch = GetMatch(test1, tes2, true);
string center1 = test1.Substring(firstmatch.Length, test1.Length -(firstmatch.Length + lasttmatch.Length)) ;
string center2 = test2.Substring(firstmatch.Length, test1.Length -(firstmatch.Length + lasttmatch.Length)) ;
}
public static string GetMatch(string fist, string second, bool isReverse)
{
if (isReverse)
{
fist = ReverseString(fist);
second = ReverseString(second);
}
StringBuilder builder = new StringBuilder();
char[] ar1 = fist.ToArray();
for (int i = 0; i < ar1.Length; i++)
{
if (fist.Length > i + 1 && ar1[i].Equals(second[i]))
{
builder.Append(ar1[i]);
}
else
{
break;
}
}
if (isReverse)
{
return ReverseString(builder.ToString());
}
return builder.ToString();
}
public static string ReverseString(string s)
{
char[] arr = s.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
Pseudo code of what you need..
int stringpos = 0
string resultstart = ""
while not end of string (either of the two)
{
if string1.substr(stringpos) == string1.substr(stringpos)
resultstart =resultstart + string1.substr(stringpos)
else
exit while
}
resultstart has you start string.. you can do the same going backwards...
Another solution you can use is Regular Expressions.
Regex re = new Regex("^bc3.*?dsc$");
String first = "bc3231dsc";
if(re.IsMatch(first)) {
//Act accordingly...
}
This gives you more flexibility when matching. The pattern above matches any string that starts in bc3 and ends in dsc with anything between except a linefeed. By changing .*? to \d, you could specify that you only want digits between the two fields. From there, the possibilities are endless.
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
class Sample {
static public void Main(){
string s1 = "bc3231dsc";
string s2 = "bc3462dsc";
List<string> common_str = commonStrings(s1,s2);
foreach ( var s in common_str)
Console.WriteLine(s);
}
static public List<string> commonStrings(string s1, string s2){
int len = s1.Length;
char [] match_chars = new char[len];
for(var i = 0; i < len ; ++i)
match_chars[i] = (Char.ToLower(s1[i])==Char.ToLower(s2[i]))? '#' : '_';
string pat = new String(match_chars);
Regex regex = new Regex("(#+)", RegexOptions.Compiled);
List<string> result = new List<string>();
foreach (Match match in regex.Matches(pat))
result.Add(s1.Substring(match.Index, match.Length));
return result;
}
}
for UPDATE CONDITION
using System;
class Sample {
static public void Main(){
string s1 = "bc3231dsc";
string s2 = "bc3462dsc";
int len = 9;//s1.Length;//cond.1)
int l_pos = 0;
int r_pos = len;
for(int i=0;i<len && Char.ToLower(s1[i])==Char.ToLower(s2[i]);++i){
++l_pos;
}
for(int i=len-1;i>0 && Char.ToLower(s1[i])==Char.ToLower(s2[i]);--i){
--r_pos;
}
string leftMatch = s1.Substring(0,l_pos);
string center1 = s1.Substring(l_pos, r_pos - l_pos);
string center2 = s2.Substring(l_pos, r_pos - l_pos);
string rightMatch = s1.Substring(r_pos);
Console.Write(
"leftMatch = \"{0}\"\n" +
"center1 = \"{1}\"\n" +
"center2 = \"{2}\"\n" +
"rightMatch = \"{3}\"\n",leftMatch, center1, center2, rightMatch);
}
}

Categories