9 times out of 10, when I want to use the Substring() method on a string, it's because I want to shave some characters off the END of the string, not the start. While the default Substring does have support for this, it does so by taking a second parameter which is the length of the desired string, rather than the endpoint. This means if I consistently want to shave off N characters off of a series of strings of differing length, I have to do an extra step, which can result in a good deal more code. Take for example:
//Shaving the first N chars
string trimmed = foo.bar.widget.GetType().Name.Substring(N);
vs.
//Shaving the last N chars
string trimmed = foo.bar.widget.GetType().Name.Substring(0, foo.bar.widget.GetType().Name.Length - N);
or maybe to save the extra function call, use a second line:
string name = foo.bar.widget.GetType().Name;
string trimmed = name.Substring(0, name.Length - N);
Either way, you're basically doubling the amount of code necessary to shave characters off the end of the string rather than the beginning. My modification would be simple. If you pass a negative N (which would otherwise be meaningless), it would shave -N characters off the end of the string instead of the beginning. I can already code this up with an extension method:
public static string MySubstring(this string str, int val)
{
return (val > 0) ? str.Substring(val) : str.Substring(0, str.Length + val);
}
And then when I want to shave off the final N chars, I just do:
string trimmed = foo.bar.widget.GetType().Name.MySubstring(-N);
Short and sweet, just like shaving off the beginning characters. My question is - would it be possible to override the behavior of the default Substring() function so that I can do this without having to use my own unique name for the function? It's not like it would invalidate any existing code, because previously there was no reason to pass it a negative number, and doing so would simply throw an exception and crash. This is just one of those simple no-nonsense features that feels like it should've been part of the implementation to begin with.
According to C# documentation, you can use extension methods to extend a class or interface, but not to override them. An extension method with the same name and signature as an interface or class method will never be called. So the answer is "No".
Arguably, this is a good thing™, because otherwise your code would become a nightmare to read to someone not familiar with your extension.
Note: str.Substring(0, str.Length + val); can be replaced with str.Remove(str.Length + val)
You can't override a method on string in the strict sense using extension methods, as the compiler will always choose an instance method over an extension method with the same signature when compiling a method call. However, you can achieve something close to what you want using named arguments. This should also help avoid readability issues. Here's an example
public static string Substring(this string #this, int trimFromEnd)
{
return #this.Substring(0, #this.Length - trimFromEnd);
}
// if you do
"abc".Substring(1) -> returns "bc"
// if you do
"abc".Substring(trimFromEnd: 1) -> returns "ab"
Personally, I find this a bit more readable than Substring(-1) or just Substring(varName), where varName happens to be negative.
Related
I have an application where I have to provide number of parameters in the format Name:Value
I provide the list of parameters through the Command line arguments value under "Debug" section of the project
So, it look something like that: "MyJobName" "0" "#FullFilePath:C:\MyFile.txt" "#FileType:MyFileType" "#FileDate:20200318" "#FileID:MyAppID"
One parameter is FilePath:C:\FileDir\MyFileTxt.txt
So, when the following logic is applied:
for (int i = 2; i <= args.GetLength(0) - 1; i++)
{
L.Add(args[i].Split(':')[0], args[i].Split(':')[1]);
}
My Parameter looks like that: FilePath:C, ignoring the rest of the path.
The final parameter list that I need to pass to the Stored Procedure should have "Name:Value" format
How can I fix that?
Split lets you pass the maximum array length.
See Split Split(Char[], Int32)
Splits a string into a maximum number of substrings based on the characters
in an array.
You also specify the maximum number of substrings to return.
Sample:
var keyValue = args[i].Split(new char[]{ ':' }, 2);
L.Add(keyValue[0], keyValue[1]);
This way only the first : is taken. The other : that come after it are ignored and will be part of the second item in the array.
But I honestly advise you to use a proper parameter parser, because your approach is very easy to break and very very fragile.
https://github.com/commandlineparser/commandline
Have a look at dragonfruit and Systel.CommandLine
Instead of writing your arguments parser yourself.
It’s a way to have type safe arguments in your main method.
Scott Hanselman has a great blog post about it.
The great part being your XML comments are used to generate a help message.
The moment you use Split, you exclude the delimiter from being a valid character without having to add the extra overloads to it. So if you absolutely must use a colon as your delimiter, you can either use the Split with overload as suggested above, or write extra code to address it;below is how I would parse it.
Of course, a much easier alternative (if possible) would be to change your delimiter to something you know it would never use, something like a pipe or a tilde or a backtick (|, ~, ). Then Split would work cleanly.
"#FullFilePath:C:\MyFile.txt" "#FileType:MyFileType" "#FileDate:20200318" "#FileID:MyAppID"
If your parameters always have the format #ParameterName:ParameterValue, your best bet is to parse the command line args like so:
var argumentsList = new Dictionary<string,object>();
for (int i=2; i < args.Length; i++)
{
int colonIndex = args[i].IndexOf(":");
string parameterName = args[i].Substring(0, colonIndex - 1);
string parameterValue = args[i].Substring(colonIndex + 1);
argumentsList[parameterName] = parameterValue;
}
The scope of your question centers around how to get around the colon, so however you choose to store the parameter values is up to you, I just used the dictionary as an example to help wrap up the code.
This will skip FilePath and give you C:\FileDir\MyFileTxt.txt
string.Join(":", args[i].Split(':').Skip(1));
I am doing exercise which provides solutions too however, no explanation on the code in the solution is given and cannot understand the code. Hope I can get help in understanding it
Exercise:
Write a C# program to create a new string from a given string where the first and last characters will change their positions.
Strings:
w3resource
Python
Expected output:
e3resourcw
nythoP
Solution:
public class Exercise16 {
static void Main(string[] args)
{
Console.WriteLine(first_last("w3resource"));
Console.WriteLine(first_last("Python"));
Console.WriteLine(first_last("x"));
}
public static string first_last(string ustr)
{
// code that I don't understand
return ustr.Length > 1
? ustr.Substring(ustr.Length - 1) + ustr.Substring(1, ustr.Length - 2) + ustr.Substring(0, 1) : ustr;
}
}
P.S - I am beginner in C# but not in programming overall
The ? operator is also called the conditional operator in C#. It acts like a miniature if statement letting you express the entire statement in a single expression. In this case it is used to verify that there is at least two characters in the string, otherwise it returns the single character string itself.
As for the Substring statements, consider which characters are being extracted from ustr with each call...
ustr.Substring(ustrLength - 1): extract the last character
ustr.Substring(1, ustr.Length - 2): extract all characters from the second to the second to last
ustr.Substring(0, 1): extract the first character
When concatenated in the order above you can see that the resulting string will start with the final character of the original string, followed by all characters from the second to the second to last, finally followed by the first character.
Basically it says if the length is greater than 1 then execute this:
ustr.Substring(ustr.Length - 1) + ustr.Substring(1, ustr.Length - 2) + ustr.Substring(0, 1)
If not, return this string variable:
ustr
This is an example of Conditional Operator "?:": Microsoft Docs Conditional Operator.
Substring means you get specific range of string character. For examples, you can check Substring Examples.
Really???
I've searched through https://msdn.microsoft.com/en-us/library/system.string(v=vs.110).aspx and don't see any method that can directly push a character onto the end of a string. The best I can figure is
mystr.Insert(mystr.Length, newchar.ToString());
which seems innefficient because of the overhead involved in converting the character to a string and performing string concatenation. My particular use case looks like
while (eqtn[curidx] >= '0' && eqtn[curidx] <= '9') istr.Insert(istr.Length, eqtn[curidx++].ToString());
only because I can't think of a better way to do it. Is there a better way?
Strings in .NET are immutable, so your code doesn't do anything. Every method on a String creates a new instance, it doesn't modify the existing string.
String class overrides + operator to create a new string with the character appended to the end:
istr = istr + eqtn[curidx++];
If you are doing a lot of such operations it will be more efficient to use a StringBuilder. It's basically a mutable String.
You can use the Append method to add a char to end. When you're ready, call ToString to get the constructed string.
Yes, that is absolutely right: you cannot push a character onto the end of a string because C# strings are immutable. Once you have an object, you are stuck with its value until you create a new string object.
On the other hand, creating a new string with an extra character at the end is very simple: use + operator overload that performs concatenation:
string s = "abc";
s += '9'; // s becomes "abc9"
Note that this solution is not so good for use in a loop, because if your loop runs N times you create N throw-away objects in the process. A better solution is to use StringBuilder, which provides a mutable string in C#. StringBuilder class has a convenient Append method, which pushes characters to the end of the StringBuilder. Once you are done building the string, call ToString to harvest the result as an immutable string object.
I have this problem where String.Contains returns true and String.LastIndexOf returns -1. Could someone explain to me what happened? I am using .NET 4.5.
static void Main(string[] args)
{
String wikiPageUrl = #"http://it.wikipedia.org/wiki/ʿAbd_Allāh_al-Sallāl";
if (wikiPageUrl.Contains("wikipedia.org/wiki/"))
{
int i = wikiPageUrl.LastIndexOf("wikipedia.org/wiki/");
Console.WriteLine(i);
}
}
While #sa_ddam213's answer definitely fixes the problem, it might help to understand exactly what's going on with this particular string.
If you try the example with other "special characters," the problem isn't exhibited. For example, the following strings work as expected:
string url1 = #"http://it.wikipedia.org/wiki/»Abd_Allāh_al-Sallāl";
Console.WriteLine(url1.LastIndexOf("it.wikipedia.org/wiki/")); // 7
string url2 = #"http://it.wikipedia.org/wiki/~Abd_Allāh_al-Sallāl";
Console.WriteLine(url2.LastIndexOf("it.wikipedia.org/wiki/")); // 7
The character in question, "ʿ", is called a spacing modifier letter1. A spacing modifier letter doesn't stand on its own, but modifies the previous character in the string, this case a "/". Another way to put this is that it doesn't take up its own space when rendered.
LastIndexOf, when called with no StringComparison argument, compares strings using the current culture.
When strings are compared in a culture-sensitive manner, the "/" and "ʿ" characters are not seen as two distinct characters--they're processed into one character, which does not match the parameter passed in to LastIndexOf.
When you pass in StringComparison.Ordinal to LastIndexOf, the characters are treated as distinct, due to the nature of Ordinal comparison.
Another way to make this work would be to use CompareInfo.LastIndexOf and supply the CompareOptions.IgnoreNonSpace option:
Console.WriteLine(
CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(
wikiPageUrl, #"it.wikipedia.org/wiki/", CompareOptions.IgnoreNonSpace));
// 7
Here we're saying that we don't want combining characters included in our string comparison.
As a sidenote, this means that #Partha's answer and #Noctis' answer only work because the character is being applied to a character that doesn't appear in the search string that's passed to LastIndexOf.
Contrast this with the Contains method, which by default performs an Ordinal (case sensitive and culture insensitive) comparison. This explains why Contains returns true and LastIndexOf returns false.
For a fantastic overview of how strings should be manipulated in the .NET framework, check out this article.
1: Is this different than a combining character or is it a type of combining character? would appreciate if someone would clear that up for me.
Try using StringComparison.Ordinal
This will compare the string by evaluating the numeric values of the corresponding chars in each string, this should work with the special chars you have in that example string
string wikiPageUrl = #"http://it.wikipedia.org/wiki/ʿAbd_Allāh_al-Sallāl";
int i = wikiPageUrl.LastIndexOf("http://it.wikipedia.org/wiki/", StringComparison.Ordinal);
// returns 0;
The thing is C# lastindexof looks from behind.
And wikipedia.org/wiki/ is followed by ' which it takes as escape sequence. So either remove ' after wiki/ or have an # there too.
The following syntax will work( anyone )
string wikiPageUrl = #"http://it.wikipedia.org/wiki/Abd_Allāh_al-Sallāl";
string wikiPageUrl = #"http://it.wikipedia.org/wiki/#ʿAbd_Allāh_al-Sallāl";
int i = wikiPageUrl.LastIndexOf("wikipedia.org/wiki");
All 3 works
If you want a generalized solution for this problem replace ' with #' in your string before you perform any operations.
the ' characters throws it off.
This should work, when you escape the ' as \':
wikiPageUrl = #"http://it.wikipedia.org/wiki/\'Abd_Allāh_al-Sallāl";
if (wikiPageUrl.Contains("wikipedia.org/wiki/"))
{
"contains".Dump();
int i = wikiPageUrl.LastIndexOf("wikipedia.org/wiki/");
Console.WriteLine(i);
}
figure out what you want to do (remove the ', escape it, or dig deeper :) ).
I have some strings such as:
1.5555555555555
2.3421354325435354545
4.509019292
I want to format them into a string such as:
1.5555
2.3421
4.5090
I tried to use the C# String.Format but I can not get it to correctly work.
Can someone please give me the correct c# statement to accomplish this?
Thanks.
It's unclear if you'll always be dealing with numeric values. If you want to avoid parsing the strings as numbers, you might try something like this:
public static string TrimTo(string str, int maxLength)
{
if (str.Length <= maxLength)
{
return str;
}
return str.Substring(0, maxLength);
}
This will trim the provided string to six characters, if it's longer than six. This seems to be what you want, but (as Kees points out), will do something unexpected with a string like "1234567.890".
The conditional clause is necessary here because String.Substring will complain if the second index is outside of the string (if the string is shorter than maxLength, in other words).
(If you've played around with C# 3.0 extension methods at all, you might recognize this, slightly modified from the above, as an excellent opportunity for one: string trimmed = s.TrimTo(10);)
string.Format("{0:N4}",decimalValue);
Standard Numeric Format Strings
Custom Numeric Format Strings
If you convert the Strings to doubles you can use String.Format to specify how many decimal places you want to include when you reformat it as a String.
String.Format("{0:0.0000}", double.Parse("1.55555555555555"))