String.Compare doesn't work on greater case - c#

I'm trying to compare string values:
using System;
public class Test
{
public static void Main()
{
int cmpValue = String.Compare("N-1.2.0.10", "N-1.2.0.8", StringComparison.InvariantCultureIgnoreCase);
if(cmpValue > 0)
Console.WriteLine("greater");
cmpValue = String.Compare("N-1.2.0.10", "N-1.2.1.10", StringComparison.InvariantCultureIgnoreCase);
if(cmpValue < 0)
Console.WriteLine("lesser");
cmpValue = String.Compare("N-1.2.0.10", "N-1.2.0.10", StringComparison.InvariantCultureIgnoreCase);
if(cmpValue == 0)
Console.WriteLine("equal");
}
}
This prints:
lesser
equal
For some reason, the greater case doesn't print. Why isn't "N-1.2.0.10" considered greater than "N-1.2.0.8"?

Why would it? String comparisons are performed character by character (with a few possiblt exceptions that do not apply here), and '1' compares as less than '8'.
The type of sorting you're looking for, where "10" compares as greater than "8", is often called a "natural sort", for which .NET Framework doesn't provide any options directly, but which is easy to create yourself (or let the native WinAPI do the work).

That happens because the strings are ordered alphabetically, not numerically:
10
11
12
20
8
etc.

Related

How to compare the elements of two string lists in C# and see if any element is convertible to double

Hello I'm making a quiz app and I want to compare user answers to the correct answers. I'm planning to do that with two string lists as I collect the answers in strings and I also have the correct answers in strings.
The problem that I'm facing is that half of the questions are True/False questions so the answers are in "True" or "False" strings, the other half are questions where a calculation has to be performed, therefore the answer is a number/double which I store as a string.
I want to give a range of answer acceptance of 0.1 so if the correct answer is 22.5 an answer of 22.6 would still be considered correct. This, however, makes it impossible to compare the value of the "number" strings with the Equals() method.
So I'm ideally looking for an if statement where I want to say:
if the element is convertible to a double convert it and check if its value is the same as the correct answer or within the acceptance range
else, check if the element is equal to the correct answer
For now I have made a console app to try and solve this problem where I have defined the two lists which look like this:
static void Main(string[] args)
{
List<string> givenAnswers = new List<string>()
{
"True","False","True","False","True",
"60","50","2.321","0.8","1.55"
};
List<string> correctAnswers = new List<string>()
{
"True","False","False","True","False",
"70","20","1.231","0.5","1.25"
};
correctAnswers.ForEach(Console.WriteLine);
Console.ReadLine();
}
The main problem is that c# doesnt return false if the element is not convertible but gives error which breaks the program.
(I was previously making the comparison on my frontend - java script where an unsuccessful conversion would return false)
Let's extract a method to compare two answers:
private static bool IsCorrectAnswer(string answer,
string expectedAnswer,
double tolerance = 0.1) {
// Let's not be peevish and tolerate leading / trimming spaces,
// ignore cases i.e. let " TRUE " be equal to "True"
if (string.Equals(answer?.Trim(),
expectedAnswer?.Trim(),
StringComparison.OrdinalIgnoreCase))
return true;
// If we have two double values, let's to parse them and compare
// with provided tolerance
if (double.TryParse(answer, out var leftValue) &&
double.TryParse(expectedAnswer, out var rightValue))
return Math.Abs(leftValue - rightValue) <= tolerance;
//TODO: you may want to add support for other types (say, dates) here
return false;
}
I didn't get your string list of booleans and numbers, but the general check would look like this:
foreach (var answer in givenAnswers)
{
if (double.TryParse(answer, NumberStyles.Any, CultureInfo.InvariantCulture, out double answerVal) && answerVal == 0.8)
{
Console.WriteLine("Nailed it!");
}
}
I had to use "invariant culture" because in my country, 0.8 is written as "0,8" and thus "0.8" is parses to 8...
I wouldn't go into lengths about the "nearby" numbers, because it's very complicated. Comparing Floating Point Numbers, 2012 Edition
You can use double.TryParse to check if a string is parsable to a double and parse it at the same time when it is.
bool IsAcceptableAnswser(string givenAnswer, string correctAnswer)
{
if (double.TryParse(correctAnswer, out var correctNumber) && double.TryParse(givenAnswer, out var givenNumber))
{
// When the answers are doubles, use use the double values.
var diff = Math.Abs(correctNumber - givenNumber);
return diff <= 0.1;
}
else
{
// When the answers aren't doubles, use string.Equals.
return string.Equals(givenAnswer, correctAnswer);
}
Notes
I used string.Equals for simplicity, but you could of course use something else to be more permissive around casing.
You might also want to check the other overloads of double.TryParse if culture is an issue.
I used 0.1 directly for readability. It would be preferable to use a constant or receive it as parameter.
Try:
var convertible = int.TryParse("60", out_)

Check if all the letters in a string are capital recursively

I have to check if all the letters are capital letters in recursion, and i dont know why this isnt working:
public static bool IsCapital(string str)
{
if (str.Length == 1)
return int.Parse(str[0].ToString()) > 65 && int.Parse(str[0].ToString()) < 90;
return IsCapital(str.Substring(1)) && int.Parse(str[0].ToString()) > 65 && int.Parse(str[0].ToString()) < 90;
}
It crashes and says: "Unhandled exception: System.FormatException: Input string was not in a correct format."
Console.WriteLine(IsCapital("abc"));
thanks.
To solely address the exception, just don't parse strings. You can directly compare a char to any ushort value.
In other words, this is a valid check (without string parsing)
str[0] > 65
AsciiTable.com should show you why the checks you have will fail on the edges.
Also consider...
Characters that aren't letters.
IsCapital(null)
Finally, something that might make this easier (assuming non-letters get bypassed) is to create a method along the lines of bool IsNotLowerCase(char c).
Note -- these are all assuming ASCII, as evident by my link.
If you must support full Unicode, hopefully you can use the methods of char.
You're attempting to parse a char as an int, instead of casting it to an int.
What you're doing is taking a letter, such as A, and parsing that as an int. A isn't a number in any way, and therefore the parse fails.
What you'll want to do is explicitly cast the char as an int to get the ASCII value you're looking for:
if (str.Length == 1)
{
return ((int)str[0]) > 65
&& ((int)str[0]) < 90;
}
return IsCapital(str.Substring(1))
&& ((int)str[0]) > 65
&& ((int)str[0]) < 90;
I assume what you're trying to do with int.Parse(str[0].ToString()) is to get the ASCII value.
What you need to use instead is this (int)str[0]
A parse will try to translate the string into a number, so a string that has a value of '412' will get parsed into an int of value 412.
I have to check if all the letters are capital letters in recursion
public static bool IsCapital(string str)
{
if (String.IsNullOrEmpty(str)) return false;
if (str.Length == 1 && char.IsUpper(str[0])) return true;
return char.IsUpper(str[0]) ? IsCapital(str.Substring(1)) :false;
}
I thinks, that number comparison will not work.
First it will return false for string like this "ABC123" even if all letters are capitals. And second there are many national characters that does not fall into range 65..90 even if they are capitals. You should (if you can) use some Char method
http://msdn.microsoft.com/en-us/library/d1x97616.aspx
While there are many ways to skin this cat, I prefer to wrap such code into reusable extension methods that make it trivial to do going forward. When using extension methods, you can also avoid RegEx as it is slower than a direct character check. I like using the extensions in the Extensions.cs NuGet package. It makes this check as simple as:
Add the [https://www.nuget.org/packages/Extensions.cs][1] package to your project.
Add "using Extensions;" to the top of your code.
"smith".IsUpper() will return False whereas "SMITH".IsUpper() will return True. 4. Every other check in the rest of the code is simply MyString.IsUpper().
Your example code would become as simple as:
using Extensions;
//Console.WriteLine(IsCapital("abc"));
Console.WriteLine("abc".IsUpper());
public static bool IsCapital(string str)
{
return !Regex.IsMatch(str, "[a..z]");
}

Is String.Equals(string1.Substring(0, x), string2) better than string1.StartsWith(string2)?

I am using string comparisons to test URL paths using StringComparison.OrdinalIgnoreCase.
MSDN gives the following string comparison advice HERE, but does not clarify WHY:
MSDN Example (half-way down the above page):
public static bool IsFileURI(string path)
{
path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
return true;
}
MSDN Advice:
"However, the preceding example uses the String.StartsWith(String, StringComparison) method to test for equality. Because the purpose of the comparison is to test for equality instead of ordering the strings, a better alternative is to call the Equals method, as shown in the following example."
public static bool IsFileURI(string path)
{
if (path.Length < 5) return false;
return String.Equals(path.Substring(0, 5), "FILE:",
StringComparison.OrdinalIgnoreCase);
}
QUESTION: Why does MSDN suggest the second example is better?
Discussion points:
Clearly the return true; in the first example is a bug and should be return path.StartsWith(...);. We can safely ignore this as a bug as the VB code is correct.
Creation of a substring prior to comparing for equality would appear to only use another memory resource than just calling String.StartsWith().
The Length < 5 test is a nice short-circuit, however it could be used with the prior code just the same.
The second example could be construed as clearer code, but I am concerned with performance. The creation of the substring seems unnecessary.
Looking at the StartsWith method using dotPeek, it eventually calls an internal comparison function that compares the entire string, and returns a boolean result based on the return value of that comparison:
return TextInfo.CompareOrdinalIgnoreCaseEx(this, 0, value, 0, value.Length, value.Length) == 0;
String.Equals calls:
return TextInfo.CompareOrdinalIgnoreCase(this, value) == 0;
CompareOrdinalIgnoreCase calls a private method, which dotPeek doesn't show, but my hunch is that the overload called by StartsWith traverses the entire string while the overload called by Equals stops as soon as equality can be determined.
If performance is a concern, try measuring both with values that will be typical for your application.
Out of curiousity, I tried measuring the two, and it does seem that Equals is noticeably faster. When I run the code below using a release build, Equals is nearly twice as fast as StartsWith:
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var url = "http://stackoverflow.com/questions/8867710/is-string-equalsstring1-substring0-x-string2-better-than-string1-startswit";
var count = 10000000;
var http = false;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
http = url.StartsWith("http:", StringComparison.OrdinalIgnoreCase);
}
sw.Stop();
Console.WriteLine("StartsWith: {0} ms", sw.ElapsedMilliseconds);
sw.Restart();
for (int i = 0; i < count; i++)
{
http = string.Equals(url.Substring(0, 5), "http:", StringComparison.OrdinalIgnoreCase);
}
sw.Stop();
Console.WriteLine("Equals: {0} ms", sw.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
The answer is found immediately below the code example you provided in under the Ordinal String Operations heading.
Strings in the .NET Framework can contain embedded null characters.
One of the clearest differences between ordinal and culture-sensitive
comparison (including comparisons that use the invariant culture)
concerns the handling of embedded null characters in a string. These
characters are ignored when you use the String.Compare and
String.Equals methods to perform culture-sensitive comparisons
(including comparisons that use the invariant culture). As a result,
in culture-sensitive comparisons, strings that contain embedded null
characters can be considered equal to strings that do not.
Important
Although string comparison methods disregard embedded null
characters, string search methods such as String.Contains,
String.EndsWith, String.IndexOf, String.LastIndexOf, and
String.StartsWith do not.
Meaning that String.StartsWith and String.Equals that use ordinal comparisons will return different results. That is, the whole point of using ordinal comparisons was to prevent people on Turkish systems circumventing security concerns (where "f" != "F" when ignoring case). Meaning someone could still circumvent security concerns by passing a file URI that looked like "FIL\0E:" if the test used String.StartsWith

C# order string

How can I determine which string is before another if ordered alphabetically?
Examples:
is "add" before "ask"? true
is "aaaabba" before "aaac"? true
is "xeon" before "alphabet"? false
Is there something in .NET that can do this? Or does someone know of a good method to do this? Thanks in advance. I am using C#, but another language is fine for examples, or even pseudocode, or an idea, thanks.
You could use the Compare static method or CompareTo instance method:
class Program
{
static void Main()
{
Console.WriteLine(string.Compare("add", "ask") < 0);
Console.WriteLine(string.Compare("aaaabba", "aaac") < 0);
Console.WriteLine(string.Compare("xeon", "alphabet") < 0);
}
}
Prints:
True
True
False
Cation at comparing culture-aware strings !
String s1 = "Strasse";
String s2 = "Straße";
var res = String.Compare(s1, s2, StringComparison.Ordinal) == 0; // returns false
CultureInfo ci = new CultureInfo("de-DE");
eq = String.Compare(s1, s2, true, ci) == 0; // returns true
Use string.Compare method
static void Main(string[] args)
{
Console.WriteLine(
"is 'add' before 'ask'? {0}",
string.Compare("add", "ask") < 0
);
Console.WriteLine(
"is 'aaaabba' before 'aaac'? {0}",
string.Compare("aaaabba", "aaac") < 0
);
Console.WriteLine(
"is 'xeon' before 'alphabet'? {0}",
string.Compare("xeon", "alphabet") < 0
);
Console.ReadLine();
}
Use the StringComparer class
http://msdn.microsoft.com/en-us/library/system.stringcomparer.aspx
You can just use
int result = String.Compare(stringa, stringb);
If result > 0, then stringa would be before stringb, < 0 stringb before stringa and 0 for when they are the same.
string.Compare is the function you're looking for.
If the result is greater than 0, then the first argument is greater than the second one. If it's lower than 0 then it's the second one. If the function returns 0, then both arguments are equal.
If you are using some container then you can check index value to check this is it index before or not like this:
if( Array.IndexOf(strArray, "C") < Array.IndexOf(strArray, "C") ) return true;
As you know it is Ordered according to alphabet then you can use String.Compare Method (String, String)
retun string.Compare(string1, string2) < 0 ? true : false
The comparison uses the current culture to obtain culture-specific information such as casing rules and the alphabetic order of individual characters. For example, a culture could specify that certain combinations of characters be treated as a single character, or uppercase and lowercase characters be compared in a particular way, or that the sorting order of a character depends on the characters that precede or follow it.
When comparing strings, you should call the Compare method, which requires that you explicitly specify the type of string comparison that the method uses. For more information, see Best Practices for Using Strings in the .NET Framework.

substring help in c#

I have string qty__c which may or may not have a decimal point
The code below gives me a System.ArgumentOutOfRangeException: Length cannot be less than zero.
qty__c = qty__c.Substring(0, qty__c.IndexOf("."));
How do i cater to if there is no "."?
Thanks
The simplest way is just to test it separately:
int dotIndex = quantity.IndexOf('.');
if (dotIndex != -1)
{
quantity = quantity.Substring(0, dotIndex);
}
There are alternatives though... for example if you really wanted to do it in a single statement, you could either use a conditional operator above, or:
quantity = quantity.Split('.')[0];
or slightly more efficiently:
// C# 4 version
quantity = quantity.Split(new[] {'.'}, 2)[0];
// Pre-C# 4 version
quantity = quantity.Split(new char[] {'.'}, 2)[0];
(The second form effectively stops splitting after finding the first dot.)
Another option would be to use regular expressions.
On the whole, I think the first approach is the most sensible though. If you find you need to do this often, consider writing a method to encapsulate it:
// TODO: Think of a better name :)
public static string SubstringBeforeFirst(string text, string delimiter)
{
int index = text.IndexOf(delimiter);
return index == -1 ? text : text.Substring(0, index);
}
You just have to test if qty__c have a point in it before calling Substring :
var pointPos = qty__c.IndexOf('.');
if (pointPos != -1)
{
qty__c = qty__c.Substring(0, pointPos);
}
Use IndexOf method on that string. If returned value is -1, there is no character that was searched.
int index = mystring.IndexOf('.');
In your code, you are not checking the returned value of IndexOf. In the case where '.' is not present in the string, an exception will be thrown because SubString has been passed -1 as second parameter.
var indexofstring=quantity.Indexof('.');
if(indexofstring!=-1)
{
quantity=quantity.SubString(0,indexofstring);
}
Assuming you are looking for the decimal point in a number, which is at offset 3 both in
'123.456' and '123', then one solution is
var index = (mystring & ".").IndexOf(".")
(Sorry about the VB, I'm not sure of C# syntax)

Categories