I was trying to learn delegate multicasting and wrote this sample program :
delegate string strDelegate(string str);
class strOps
{
public static string reverseString(string str)
{
string temp = string.Empty;
for(int i=str.Length -1 ; i>=0 ; i--)
{
temp += str[i];
}
return temp;
}
public string removeSpaces(string str)
{
string temp = string.Empty;
for (int i = 0; i < str.Length; i++)
{
if (str[i] != ' ')
temp += str[i];
}
return temp;
}
}
// calling the code in main method
string str = "This is a sample string";
strOps obj = new strOps();
strDelegate delRef = obj.removeSpaces;
delRef += strOps.reverseString;
Console.WriteLine("the result of passing This is a sample string \n {0}", delRef(str));
I expected it to return the reversed string without spaces , instead it ONLY reverses string and gives this output :
gnirts elpmas a si sihT
Can anybody please point me in the right direction to understand this.
Any help will be appreciated.
Thanks.
A combined delegate will only return the result of the last invoked method. From the documentation:
If the delegate has a return value and/or out parameters, it returns
the return value and parameters of the last method invoked
The multicast delegate will still invoke both methods assigned to it. If you change your methods to print the value before returning it, you'll see it clearly:
void Main()
{
string str = "This is a sample string";
strOps obj = new strOps();
strDelegate delRef = obj.removeSpaces;
delRef += strOps.reverseString;
delRef(str);
}
delegate string strDelegate(string str);
class strOps
{
public static string reverseString(string str)
{
string temp = string.Empty;
for(int i=str.Length -1 ; i>=0 ; i--)
{
temp += str[i];
}
Console.WriteLine("Output from ReverseString: {0}", temp);
return temp;
}
public string removeSpaces(string str)
{
string temp = string.Empty;
for (int i = 0; i < str.Length; i++)
{
if (str[i] != ' ')
temp += str[i];
}
Console.WriteLine("Output from RemoveSpaces: {0}", temp);
return temp;
}
}
Outputs:
Output from RemoveSpaces: Thisisasamplestring
Output from ReverseString: gnirts elpmas a si sihT
I would like to match strings with a wildcard (*), where the wildcard means "any". For example:
*X = string must end with X
X* = string must start with X
*X* = string must contain X
Also, some compound uses such as:
*X*YZ* = string contains X and contains YZ
X*YZ*P = string starts with X, contains YZ and ends with P.
Is there a simple algorithm to do this? I'm unsure about using regex (though it is a possibility).
To clarify, the users will type in the above to a filter box (as simple a filter as possible), I don't want them to have to write regular expressions themselves. So something I can easily transform from the above notation would be good.
Often, wild cards operate with two type of jokers:
? - any character (one and only one)
* - any characters (zero or more)
so you can easily convert these rules into appropriate regular expression:
// If you want to implement both "*" and "?"
private static String WildCardToRegular(String value) {
return "^" + Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*") + "$";
}
// If you want to implement "*" only
private static String WildCardToRegular(String value) {
return "^" + Regex.Escape(value).Replace("\\*", ".*") + "$";
}
And then you can use Regex as usual:
String test = "Some Data X";
Boolean endsWithEx = Regex.IsMatch(test, WildCardToRegular("*X"));
Boolean startsWithS = Regex.IsMatch(test, WildCardToRegular("S*"));
Boolean containsD = Regex.IsMatch(test, WildCardToRegular("*D*"));
// Starts with S, ends with X, contains "me" and "a" (in that order)
Boolean complex = Regex.IsMatch(test, WildCardToRegular("S*me*a*X"));
You could use the VB.NET Like-Operator:
string text = "x is not the same as X and yz not the same as YZ";
bool contains = LikeOperator.LikeString(text,"*X*YZ*", Microsoft.VisualBasic.CompareMethod.Binary);
Use CompareMethod.Text if you want to ignore the case.
You need to add using Microsoft.VisualBasic.CompilerServices; and add a reference to the Microsoft.VisualBasic.dll.
Since it's part of the .NET framework and will always be, it's not a problem to use this class.
For those using .NET Core 2.1+ or .NET 5+, you can use the FileSystemName.MatchesSimpleExpression method in the System.IO.Enumeration namespace.
string text = "X is a string with ZY in the middle and at the end is P";
bool isMatch = FileSystemName.MatchesSimpleExpression("X*ZY*P", text);
Both parameters are actually ReadOnlySpan<char> but you can use string arguments too. There's also an overloaded method if you want to turn on/off case matching. It is case insensitive by default as Chris mentioned in the comments.
Using of WildcardPattern from System.Management.Automation may be an option.
pattern = new WildcardPattern(patternString);
pattern.IsMatch(stringToMatch);
Visual Studio UI may not allow you to add System.Management.Automation assembly to References of your project. Feel free to add it manually, as described here.
A wildcard * can be translated as .* or .*? regex pattern.
You might need to use a singleline mode to match newline symbols, and in this case, you can use (?s) as part of the regex pattern.
You can set it for the whole or part of the pattern:
X* = > #"X(?s:.*)"
*X = > #"(?s:.*)X"
*X* = > #"(?s).*X.*"
*X*YZ* = > #"(?s).*X.*YZ.*"
X*YZ*P = > #"(?s:X.*YZ.*P)"
*X*YZ* = string contains X and contains YZ
#".*X.*YZ"
X*YZ*P = string starts with X, contains YZ and ends with P.
#"^X.*YZ.*P$"
It is necessary to take into consideration, that Regex IsMatch gives true with XYZ, when checking match with Y*. To avoid it, I use "^" anchor
isMatch(str1, "^" + str2.Replace("*", ".*?"));
So, full code to solve your problem is
bool isMatchStr(string str1, string str2)
{
string s1 = str1.Replace("*", ".*?");
string s2 = str2.Replace("*", ".*?");
bool r1 = Regex.IsMatch(s1, "^" + s2);
bool r2 = Regex.IsMatch(s2, "^" + s1);
return r1 || r2;
}
This is kind of an improvement on the popular answer from #Dmitry Bychenko above (https://stackoverflow.com/a/30300521/4491768). In order to support ? and * as a matching characters we have to escape them. Use \\? or \\* to escape them.
Also a pre compiled regex will improve the performance (on reuse).
public class WildcardPattern
{
private readonly string _expression;
private readonly Regex _regex;
public WildcardPattern(string pattern)
{
if (string.IsNullOrEmpty(pattern)) throw new ArgumentNullException(nameof(pattern));
_expression = "^" + Regex.Escape(pattern)
.Replace("\\\\\\?","??").Replace("\\?", ".").Replace("??","\\?")
.Replace("\\\\\\*","**").Replace("\\*", ".*").Replace("**","\\*") + "$";
_regex = new Regex(_expression, RegexOptions.Compiled);
}
public bool IsMatch(string value)
{
return _regex.IsMatch(value);
}
}
usage
new WildcardPattern("Hello *\\**\\?").IsMatch("Hello W*rld?");
new WildcardPattern(#"Hello *\**\?").IsMatch("Hello W*rld?");
To support those one with C#+Excel (for partial known WS name) but not only - here's my code with wildcard (ddd*).
Briefly: the code gets all WS names and if today's weekday(ddd) matches the first 3 letters of WS name (bool=true) then it turn it to string that gets extracted out of the loop.
using System;
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using Range = Microsoft.Office.Interop.Excel.Range;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.Text.RegularExpressions;
...
string weekDay = DateTime.Now.ToString("ddd*");
Workbook sourceWorkbook4 = xlApp.Workbooks.Open(LrsIdWorkbook, 0, false, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
Workbook destinationWorkbook = xlApp.Workbooks.Open(masterWB, 0, false, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
static String WildCardToRegular(String value)
{
return "^" + Regex.Escape(value).Replace("\\*", ".*") + "$";
}
string wsName = null;
foreach (Worksheet works in sourceWorkbook4.Worksheets)
{
Boolean startsWithddd = Regex.IsMatch(works.Name, WildCardToRegular(weekDay + "*"));
if (startsWithddd == true)
{
wsName = works.Name.ToString();
}
}
Worksheet sourceWorksheet4 = (Worksheet)sourceWorkbook4.Worksheets.get_Item(wsName);
...
public class Wildcard
{
private readonly string _pattern;
public Wildcard(string pattern)
{
_pattern = pattern;
}
public static bool Match(string value, string pattern)
{
int start = -1;
int end = -1;
return Match(value, pattern, ref start, ref end);
}
public static bool Match(string value, string pattern, char[] toLowerTable)
{
int start = -1;
int end = -1;
return Match(value, pattern, ref start, ref end, toLowerTable);
}
public static bool Match(string value, string pattern, ref int start, ref int end)
{
return new Wildcard(pattern).IsMatch(value, ref start, ref end);
}
public static bool Match(string value, string pattern, ref int start, ref int end, char[] toLowerTable)
{
return new Wildcard(pattern).IsMatch(value, ref start, ref end, toLowerTable);
}
public bool IsMatch(string str)
{
int start = -1;
int end = -1;
return IsMatch(str, ref start, ref end);
}
public bool IsMatch(string str, char[] toLowerTable)
{
int start = -1;
int end = -1;
return IsMatch(str, ref start, ref end, toLowerTable);
}
public bool IsMatch(string str, ref int start, ref int end)
{
if (_pattern.Length == 0) return false;
int pindex = 0;
int sindex = 0;
int pattern_len = _pattern.Length;
int str_len = str.Length;
start = -1;
while (true)
{
bool star = false;
if (_pattern[pindex] == '*')
{
star = true;
do
{
pindex++;
}
while (pindex < pattern_len && _pattern[pindex] == '*');
}
end = sindex;
int i;
while (true)
{
int si = 0;
bool breakLoops = false;
for (i = 0; pindex + i < pattern_len && _pattern[pindex + i] != '*'; i++)
{
si = sindex + i;
if (si == str_len)
{
return false;
}
if (str[si] == _pattern[pindex + i])
{
continue;
}
if (si == str_len)
{
return false;
}
if (_pattern[pindex + i] == '?' && str[si] != '.')
{
continue;
}
breakLoops = true;
break;
}
if (breakLoops)
{
if (!star)
{
return false;
}
sindex++;
if (si == str_len)
{
return false;
}
}
else
{
if (start == -1)
{
start = sindex;
}
if (pindex + i < pattern_len && _pattern[pindex + i] == '*')
{
break;
}
if (sindex + i == str_len)
{
if (end <= start)
{
end = str_len;
}
return true;
}
if (i != 0 && _pattern[pindex + i - 1] == '*')
{
return true;
}
if (!star)
{
return false;
}
sindex++;
}
}
sindex += i;
pindex += i;
if (start == -1)
{
start = sindex;
}
}
}
public bool IsMatch(string str, ref int start, ref int end, char[] toLowerTable)
{
if (_pattern.Length == 0) return false;
int pindex = 0;
int sindex = 0;
int pattern_len = _pattern.Length;
int str_len = str.Length;
start = -1;
while (true)
{
bool star = false;
if (_pattern[pindex] == '*')
{
star = true;
do
{
pindex++;
}
while (pindex < pattern_len && _pattern[pindex] == '*');
}
end = sindex;
int i;
while (true)
{
int si = 0;
bool breakLoops = false;
for (i = 0; pindex + i < pattern_len && _pattern[pindex + i] != '*'; i++)
{
si = sindex + i;
if (si == str_len)
{
return false;
}
char c = toLowerTable[str[si]];
if (c == _pattern[pindex + i])
{
continue;
}
if (si == str_len)
{
return false;
}
if (_pattern[pindex + i] == '?' && c != '.')
{
continue;
}
breakLoops = true;
break;
}
if (breakLoops)
{
if (!star)
{
return false;
}
sindex++;
if (si == str_len)
{
return false;
}
}
else
{
if (start == -1)
{
start = sindex;
}
if (pindex + i < pattern_len && _pattern[pindex + i] == '*')
{
break;
}
if (sindex + i == str_len)
{
if (end <= start)
{
end = str_len;
}
return true;
}
if (i != 0 && _pattern[pindex + i - 1] == '*')
{
return true;
}
if (!star)
{
return false;
}
sindex++;
continue;
}
}
sindex += i;
pindex += i;
if (start == -1)
{
start = sindex;
}
}
}
}
C# Console application sample
Command line Sample:
C:/> App_Exe -Opy PythonFile.py 1 2 3
Console output:
Argument list: -Opy PythonFile.py 1 2 3
Found python filename: PythonFile.py
using System;
using System.Text.RegularExpressions; //Regex
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string cmdLine = String.Join(" ", args);
bool bFileExtFlag = false;
int argIndex = 0;
Regex regex;
foreach (string s in args)
{
//Search for the 1st occurrence of the "*.py" pattern
regex = new Regex(#"(?s:.*)\056py", RegexOptions.IgnoreCase);
bFileExtFlag = regex.IsMatch(s);
if (bFileExtFlag == true)
break;
argIndex++;
};
Console.WriteLine("Argument list: " + cmdLine);
if (bFileExtFlag == true)
Console.WriteLine("Found python filename: " + args[argIndex]);
else
Console.WriteLine("Python file with extension <.py> not found!");
}
}
}
Total string length is 5 chars
I have a scenario, ID starts with
A0001 and ends with A9999 then
B0001 to B9999 until F0001 to f9999
after that
FA001 to FA999 then
FB001 to FB999 until ....FFFF9
Please suggest any idea on how to create this format.
public static IEnumerable<string> Numbers()
{
return Enumerable.Range(0xA0000, 0xFFFF9 - 0xA0000 + 1)
.Select(x => x.ToString("X"));
}
You could also have an id generator class:
public class IdGenerator
{
private const int Min = 0xA0000;
private const int Max = 0xFFFF9;
private int _value = Min - 1;
public string NextId()
{
if (_value < Max)
{
_value++;
}
else
{
_value = Min;
}
return _value.ToString("X");
}
}
I am a few years late. But I hope my answer will help everyone looking for a good ID Generator. None of the previous answers work as expected and do not answer this question.
My answer fits the requirements perfectly. And more!!!
Notice that setting the _fixedLength to ZERO will create dynamically sized ID's.
Setting it to anything else will create FIXED LENGTH ID's;
Notice also that calling the overload that takes a current ID will "seed" the class and consecutive calls DO NOT need to be called with another ID. Unless you had random ID's and need the next one on each.
Enjoy!
public static class IDGenerator
{
private static readonly char _minChar = 'A';
private static readonly char _maxChar = 'C';
private static readonly int _minDigit = 1;
private static readonly int _maxDigit = 5;
private static int _fixedLength = 5;//zero means variable length
private static int _currentDigit = 1;
private static string _currentBase = "A";
public static string NextID()
{
if(_currentBase[_currentBase.Length - 1] <= _maxChar)
{
if(_currentDigit <= _maxDigit)
{
var result = string.Empty;
if(_fixedLength > 0)
{
var prefixZeroCount = _fixedLength - _currentBase.Length;
if(prefixZeroCount < _currentDigit.ToString().Length)
throw new InvalidOperationException("The maximum length possible has been exeeded.");
result = result = _currentBase + _currentDigit.ToString("D" + prefixZeroCount.ToString());
}
else
{
result = _currentBase + _currentDigit.ToString();
}
_currentDigit++;
return result;
}
else
{
_currentDigit = _minDigit;
if(_currentBase[_currentBase.Length - 1] == _maxChar)
{
_currentBase = _currentBase.Remove(_currentBase.Length - 1) + _minChar;
_currentBase += _minChar.ToString();
}
else
{
var newChar = _currentBase[_currentBase.Length - 1];
newChar++;
_currentBase = _currentBase.Remove(_currentBase.Length - 1) + newChar.ToString();
}
return NextID();
}
}
else
{
_currentDigit = _minDigit;
_currentBase += _minChar.ToString();
return NextID();
}
}
public static string NextID(string currentId)
{
if(string.IsNullOrWhiteSpace(currentId))
return NextID();
var charCount = currentId.Length;
var indexFound = -1;
for(int i = 0; i < charCount; i++)
{
if(!char.IsNumber(currentId[i]))
continue;
indexFound = i;
break;
}
if(indexFound > -1)
{
_currentBase = currentId.Substring(0, indexFound);
_currentDigit = int.Parse(currentId.Substring(indexFound)) + 1;
}
return NextID();
}
}
This is a sample of the ouput using _fixedLength = 4 and _maxDigit = 5
A001
A002
A003
A004
A005
B001
B002
B003
B004
B005
C001
C002
C003
C004
C005
AA01
AA02
AA03
AA04
AA05
AB01
AB02
AB03
AB04
AB05
AC01
AC02
AC03
AC04
AC05
see this code
private void button1_Click(object sender, EventArgs e)
{
string get = label1.Text.Substring(7); //label1.text=ATHCUS-100
MessageBox.Show(get);
string ou="ATHCUS-"+(Convert.ToInt32(get)+1).ToString();
label1.Text = ou.ToString();
}
Run this query in order to get the last ID in the database
SELECT TOP 1 [ID_COLUMN] FROM [NAME_OF_TABLE] ORDER BY [ID_COLUMN] DESC
Read the result to a variable and then run the following function on the result in order to get the next ID.
public string NextID(string lastID)
{
var allLetters = new string[] {"A", "B", "C", "D", "E", "F"};
var lastLetter = lastID.Substring(0, 1);
var lastNumber = int.Parse(lastID.Substring(1));
if (Array.IndexOf(allLetters, lastLetter) < allLetters.Length - 1 &&
lastNumber == 9999)
{
//increase the letter
lastLetter = allLetters(Array.IndexOf(allLetters, lastLetter) + 1);
lastNumber = 0;
} else {
lastLetter = "!";
}
var result = lastLetter + (lastNumber + 1).ToString("0000");
//ensure we haven't exceeded the upper limit
if (result.SubString(0, 1) == "!") {
result = "Upper Bounds Exceeded!";
}
return result;
}
DISCLAIMER
This code will only generate the first set of IDs. I do not understand the process of generating the second set.
If you need to take it from the database and do this you can use something like the following.
int dbid = /* get id from db */
string id = dbid.ToString("X5");
This should give you the format you are looking for as a direct convert from the DB ID.
Consider the following code:
string[] s = new[] { "Rob", "Jane", "Freddy" };
string joined = string.Join(", ", s);
// joined equals "Rob, Jane, Freddy"
For UI reasons I might well want to display the string "Rob, Jane or Freddy".
Any suggestions about the most concise way to do this?
Edit
I am looking for something that is concise to type. Since I am only concatenating small numbers (<10) of strings I am not worried about run-time performance here.
Concise meaning to type? or to run? The fastest to run will be hand-cranked with StringBuilder. But to type, probably (edit handle 0/1 etc):
string joined;
switch (s.Length) {
case 0: joined = ""; break;
case 1: joined = s[0]; break;
default:
joined = string.Join(", ", s, 0, s.Length - 1)
+ " or " + s[s.Length - 1];
break;
}
The StringBuilder approach might look something like:
static string JoinOr(string[] values) {
switch (values.Length) {
case 0: return "";
case 1: return values[0];
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.Length - 2; i++) {
sb.Append(values[i]).Append(", ");
}
return sb.Append(values[values.Length-2]).Append(" or ")
.Append(values[values.Length-1]).ToString();
}
Concatenate all but the last one. Do the last one manually.
Create an extension method on string[] that implement the same logic as string.Join but the last item will be appended with "or".
string[] s = new[] { "Rob", "Jane", "Freddy" };
Console.WriteLine(s.BetterJoin(", ", " or "));
// ---8<----
namespace ExtensionMethods
{
public static class MyExtensions
{
public static string BetterJoin(this string[] items, string separator, string lastSeparator)
{
StringBuilder sb = new StringBuilder();
int length = items.Length - 2;
int i = 0;
while (i < length)
{
sb.AppendFormat("{0}{1}", items[i++], separator);
}
sb.AppendFormat("{0}{1}", items[i++], lastSeparator);
sb.AppendFormat("{0}", items[i]);
return sb.ToString();
}
}
}
What about:
if (s.Length > 1)
{
uiText = string.Format("{0} and {1}", string.Join(", ", s, 0, s.Length - 1), s[s.Length - 1]);
}
else
{
uiText = s.Length > 0 ? s[0] : "";
}
Generic solution for any type T.
static class IEnumerableExtensions
{
public static string Join<T>(this IEnumerable<T> items,
string seperator, string lastSeperator)
{
var sep = "";
return items.Aggregate("", (current, item) =>
{
var result = String.Concat(current,
// not first OR not last
current == "" || !items.Last().Equals(item) ? sep : lastSeperator,
item.ToString());
sep = seperator;
return result;
});
}
}
Usage:
var three = new string[] { "Rob", "Jane", "Freddy" };
var two = new string[] { "Rob", "Jane" };
var one = new string[] { "Rob" };
var threeResult = three.Join(", ", " or "); // = "Rob, Jane or Freddy"
var twoResult = two.Join(", ", " or "); // = "Rob or Jane"
var oneResult = one.Join(", ", " or "); // = "Rob"
The most memory efficient and scalable would be using a StringBuilder and precalculating the length of the final string to elliminate buffer reallocations. (This is similar to how the String.Concat method works.)
public static string Join(string[] items, string separator, string lastSeparator) {
int len = separator.Length * (items.Length - 2) + lastSeparator.Length;
foreach (string s in items) len += s.Length;
StringBuilder builder = new StringBuilder(len);
for (int i = 0; i < items.Length; i++) {
builder.Append(items[i]);
switch (items.Length - i) {
case 1: break;
case 2: builder.Append(lastSeparator); break;
default: builder.Append(separator); break;
}
}
return builder.ToString();
}
Usage:
string joined = Join(s, ", ", " or ");
An interresting solution would be using a recursive algorithm. It works well for a reasonably small number of strings, but it doesn't scale very well.
public static string Join(string[] items, int index , string separator, string lastSeparator) {
return items[index++] + (index == items.Length-1 ? lastSeparator + items[index] : separator + Join(items, index, separator, lastSeparator));
}
Usage:
string joined = Join(s, 0, ", ", " or ");
string[] name_storage = new[] { "emre" , "balc" };
name_storage[name_storage.Count() - 1] += "ı";
string name = name_storage[0];
string sur_name = name_storage[1];
divElement.InnerHtml += name + " - " + sur_name;
//result = emre - balcı