So I'm doing this years Advent of Code and I'm stuck on the second day, part 2.
You are given inputs which look like this:
"1-3 c: caaasa"
You have to check how many passwords are valid due to the policy like,
in above example:
letter c has to be in position 1 OR 3 in the string caaasa. If
yes, the password is valid.
I've broken down that string to different sections, and now I try to compare a string "znak" which contains that given letter to a letter on position zakresmin and zakresmax in string "passdiv"
Yet, everytime it returns False, so it doesn't add up to the count of passwords.
I tried using Equals() and CompareTo(), but they don't seem to work.
How can I modify my code so it returns proper values?
var iloschasel = 0;
using (StreamReader sr = new StreamReader(#"C:\Users\Wurf\Desktop\text.txt"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
string[] linia = line.Split(" ");
string zakres = linia[0];
string[] zakresy = zakres.Split("-");
int zakresmin = Convert.ToInt32(zakresy[0]);
int zakresmax = Convert.ToInt32(zakresy[1]);
string znak = (linia[1].Replace(":", "")).Trim();
var suma = Regex.Matches(linia[2], znak);
string passdiv = linia[2];
if(passdiv[zakresmin].Equals(znak) || passdiv[zakresmax - 1].Equals(znak))
{
iloschasel += 1;
}
}
}
Console.WriteLine(iloschasel);
As mentioned, when you call Equals on two different types you are playing a game of chance with how the actual types are implemented. In this case you lose. Strings and chars will never have an equivalence or the same reference.
I believe the compiler or resharper would give you a warning alerting you that neither type derive from string and char
However, I was bored enough to give an alternate solution
public static bool IsValid(string input)
{
var match = Regex.Match(input, #"(\d)-(\d) (\S): (.*)");
if(!match.Success)
throw new ArgumentException( $"Invalid format : {input}",nameof(input));
var first = int.Parse(match.Groups[1].Value);
var second = int.Parse(match.Groups[2].Value);
var c = char.Parse(match.Groups[3].Value);
var password = match.Groups[4].Value;
return password[first-1] == c && password[second-1] == c;
}
Test
Console.WriteLine($"Is Valid = {IsValid("1-3 c: caaasa")}");
Console.WriteLine($"Is Valid = {IsValid("1-3 c: cacaasa")}");
Output
Is Valid = False
Is Valid = True
Note : this is not meant to be a complete bullet-proof solution. Just a novel elegant way to solve your problem
Your problem is that you are comparing a string to a char
var match = "c" == 'c';
Will give a compile error because they are different data types
var match = "c".Equals('c');
will let you compile, but will always return false because a char will never equal a string. You have to turn the char into a string or visa versa for the check to work
var match = "c"[0] == 'c';
So in your if statement, if you fix the check to compare strings with strings or chars with chars you should get some positive results. And also fix your indexing issue to decide if you want a 0 based index or a 1 based index with zakresmin and max
Also as a side note, it can be helpful to step through your code line by line in debug mode, to find out which line isn't behaving like you expect it to. In your case debugging would have helped you zero in on the if statement as a starting point to fixing things.
So it turns out (if I understand that correctly) that a compared element of the string passdiv was a char which I tried to compare to znak which was a string. I added ToString() to my code and it works well. Also fixed the range of zakresmin by subtracting 1 so it works properly.
if((passdiv[zakresmin - 1].ToString() == znak && passdiv[zakresmax - 1].ToString() != znak) || (passdiv[zakresmin - 1].ToString() != znak && passdiv[zakresmax - 1].ToString() == znak))
{
iloschasel += 1;
}
Related
I want to get result of a value with if condition.
i have get some value in xml file.
now what I want is
if I have a variable "a" here i have assigned some values by using dataset.
and i have another variable "b" is assigned value from xml file.
for example
int a=25;
string b=">10"
now I want to check the condition if condition with out ">" because the symbol present in b variable. I dont know how to check this condition can anybody explain me how to acheive this.
I tried like this but not working
if(a+b)
You can use the DataTable.Compute-"trick" to evaulate such expressions:
int a = 25;
string b = ">10";
bool isTrue = (bool)new DataTable().Compute($"{a}{b}", null); // true
What is supported you can read at the DataColumn.Expression remarks.
if the condition is 1!=10, how to use not equal in this code .this
condition is not working what should i do.
As the documentation tells you that is not valid syntax, you have to use <> (look at operators). So a simple approach would be either to use <> in the first place or replace them:
b = b.Replace("!=", "<>");
You can have some function to remove non numeric characters:
public int Parse(string x)
{
x = Regex.Replace(x, "[^0-9.]", "");
int result = 0;
int.TryParse(x , out result);
return result;
}
If its always a number with a symbol then:
symbol = b[0];
int bval = int.Parse(b.Substring(1))
And considering your comment for comparison you can do:
if((symbol=='>'&&a>b)||
(symbol=='='&&a==b)||
(symbol=='<'&&a<b)
){
//do your magic here
}
Of course you may need only one of < = > or you may need to have separate if conditions for each, what ever suits your needs, but I just wanted to give the idea.
I tried like this
if (b.Contains(">")) {
b = b.Replace(">", "");
if (a >Convert.ToInt32(b))
{
Console.WriteLine("value is less");
}
else
{
Console.WriteLine("value is Greater");
}
}
similarly all the symbols
First separate symbol from b:
string symbol = b[0].ToString();
string numberString = b.SubString(1);
int number = int.Parse(numberString);
Now use switch to get operation for symbol and compare:
bool result = false;
switch (symbol)
{
case ">":
if (a > number)
{
result = true;
}
break;
}
EDIT: Changed symbol declaration to avoid error: "cannot implicit convert type char to string"
I am seeing lots of these in a method in our code:
int num1 = 0;
if (Char.IsDigit(myStr[2]) && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{
valid = false;
}
So are they just making sure the third character us a digit?
The code shown parses the 3rd character only - checking if it is digit, then parsing the string representation of that single character. Instead, just use the numeric value of that character:
if(myStr[2] >= '0' && myStr[2] <= '9') {
num1 = (int)myStr[2] - (int)'0';
} else {
valid = false
}
You can safely skip the IsDigit() check as it's redundant.
TryParse() will fail if it's not a digit.
As it has been pointed out by others, Char.IsDigit() is quicker. If your code is performance sensitive the check makes sense.
If you leave the IsDigit check in place, then you can reduce TryParse to Int32.Parse() as at that point the parsing won't fail.
It looks like the code that you have is doing this for efficiency. Whoever coded this, knows the structure of the string in myStr to sometimes have a non-numeric symbol in the third position. That's why he made this optimization to check the third symbol before paying for the conversion of the character array to string which then gets parsed.
Chances are, this optimization is premature: although making a temporary throw-away string is not free, this optimization would make sense only in situations when you do it a lot in a very tight loop. In other words, you do it only if it shows up near the top in your performance profiler's output.
You can optimize this check to avoid if:
int num1 = 0;
valid &= !Char.IsDigit(myStr[2]) || Int32.TryParse(myStr[2].ToString(), out num1);
I don't believe you need the first part (it could also throw an IndexOutOfRangeException).
So I would probably use:
int num1 = 0;
if (myStr.Length > 2 && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{
valid = false;
}
Char.IsDigit Method (String, Int32)
Indicates whether the character at the specified position in a specified string is categorized as a decimal digit.
Link
Int32.TryParse Method
Converts the string representation of a number to its 32-bit signed integer equivalent. A return value indicates whether the operation succeeded. This member is overloaded.
Link
Edit:
First I wrote that you can skip any of the check but now I am writing that you can not because
if (Char.IsDigit(myStr[2]) && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{ }
Char.IsDigit() will return true if myStr[2] contains any of the Unicode characters listed here but Int.TryParse() will not convert any numbers except for 0-9 (not sure about this, as I have not checked all of them) so it will return false which is you are checking...
The condition you are checking can be understood by the following example:
string x = "AS௭s";
int s = 0;
if (Char.IsDigit(x[2]) && int.TryParse(x[2].ToString(), out s) == false)
{
// even if '௭` is Tamil Digit Seven and 'Char.IsDigit()' will return true but
// int.TryParse() will return false because it can not convert it
// so you are setting valid = false when the myStr contains a valid Unicode Character
// for a digit but It can not be converted to integer by TryParse method...
valid = false;
}
#Marc Gravell♦'s answer is the best solution for checking this condition...
Here's how I'd write it:
int num1 = 0;
try
{
num1 = Int32.Parse(myStr[2].ToString());
}
catch (Exception)
{
valid = false;
}
This does the same thing and is a lot easier to read imho, oh & you can log failed parses inside the catch.
Or you can do:
int num1 = 0;
valid = Int32.TryParse(myStr[2].ToString(), out num1);
This has probably (somewhere) been asked before, but can't find any documentation on it (i have looked!).
Say I had declared a string like:
String Test = "abcdefg";
How would i go about searching the string to see if I could see "cd" anywhere in the string by searching through the string in pairs, like:
{ab}{bc}{cd}{de}{ef}{fg}
That is, if I split each of the values up, and searched for a pair of chars next to each other? Is there a built in function for this?
I have thought about using a char array for this, but it seems to (logically) be very 'heavy'/'slow'. Would there be a better solution to search this string?
EDIT 1
Once I see this "cd", I would then need to doSomething() at that position (which I have already implemented by using the substring method.
Try this.
String.IndexOf(...) != -1
For more infö, read here.
Similar to the answer from Neo, but in a loop to get all instances within the string:
string Test = "abcdefgcd";
int index = Test.IndexOf("cd");
while (index > -1)
{
//DoSomething();
index = Test.IndexOf("cd", ++index);
}
The first IndexOf checks for the existence of what you want, whilst the second IndexOf (in the loop) checks for a match after the last index.
In the above we find two matches and then the loop ends.
There is no build in function that will do that.
having a for loop should do what you want.
something like that:
string str = string.empty;
for (i=0;i<ch.length;i++) {
if (i != ch.length) {
str += ch[i] + ch[i+1];
}
}
also you can use regex however that wont be fast either.
In order to optimize this on a large scale you can implement byte shifting.
The ASCII code of your string characters is your friend in this case, full working example below:
var yourString = "abcdefg";
var x = '\0';
for (var i = 0; i < yourString.Length; i++)
{
//check whether i+1 index is not out of range
if (i + 1 != yourString.Length)
{
var test = yourString[i + 1];
x = yourString[i];
if(x.ToString() + test.ToString() == "cd")
{
Console.Write("Found at position " + i)
}
}
}
Let say, * has to be followed by &.
For example,
string asd = "Mother*&Mother*&Son";
// which is "Mother+ "*&" + "Mother" + "*&" + "Son"
// This is correct string.
Bad example,
string asd = "Mother*Mother*&Son";
string asf = "Mother**&Mother*&Son";
string asg = "Mother*&*Mother*&Son";
How can I check if the string is correct or not in C#?
EDIT
based on the usage of Regex that you guys introduced, I have a side question. I am actually using comma(,) instead of asterisk(*) and quotation mark(") instead of ampersand(&).
In C#, (Let me use one of the guy's example)
Regex.IsMatch("Mother,\",Mother,\"Son", #"\,(?!")")
//won't work.. any idea?
I also tried
Regex.IsMatch("Mother,\",Mother,\"Son", #"\,(?!\")")
//not work, neither
Find failures by looking for any asterisk (*) not followed by an ampersand (&):
Regex.IsMatch("Mother*&*Mother*&Son", #"\*(?!&)")
You can use regex. But it will be easier to find when string is not correct and then just negate the result.
I would look for any * which is not followed by &. The regex should look like: (\*[^&])|(\*$)
Simple test code:
var inputs = new[] {
"Mother*&Mother*&Son",
"Mother*Mother*&Son",
"Mother**&Mother*&Son",
"Mother*&*Mother*&Son",
"Mother*&Mother*&Son*"
};
var regex = new Regex(#"(\*[^&])|(\*$)");
var isOK = inputs.Select(x => !regex.IsMatch(x)).ToList();
Returns a list of results, which contains true, false, false, false, false.
For something like this, I'd favor the direct approach, rather than using Regex. This will make at most one pass through the entire string, which should be more efficient than a Regex.
/// Return true if every instance of 'a' in the string is followed by 'b'.
/// Also returns true if there are no instances of 'a' in the string.
/// Returns false if there exists any 'a' that is not followed by 'b'.
public static bool IsTwoCharSequence(string s, char a, char b)
{
if(String.IsNullOrEmpty(s)) return true;
if(s[s.Length - 1] == a) return false; // ends in a, not followed by b. Condition failed.
int index = s.IndexOf(a); // find the first a
while(index != -1)
{
if(s[index + 1] != b) return false; // a not followed by b.
index = s.IndexOf(a, index + 1);
}
return true; // either no a, or all a followed by b.
}
Edit: In addition, you don't need to worry about how to quote your separator characters, when they're also special characters within a Regex.
Edit 2: Yes, it's two loops, but look at what each loop is doing.
The inner loop, the one inside of String.IndexOf, will iterate through characters until it finds the passed-in character. The first call to IndexOf (the one outside the while loop) starts searching at the beginning of the string, and the subsequent ones start at that index, and continue searching to the next match, or to the end. Overall, we've made just one pass over the entire string.
Here's another method, which is similar in concept to the above one, but where the 'iterate the entire string only once' is more explicit.
public static bool IsTwoCharSequence(string s, char a, char b)
{
if (String.IsNullOrEmpty(s)) return true;
bool foundA = false;
foreach (char c in s)
{
if (foundA && c == b)
foundA = false;
else if (foundA)
return false;
else if (c == a)
foundA = true;
}
if (foundA) return false; // 'a' was the last char in the string.
return true;
}
Use Regular expressions and check that the number of matches for *& is the same as the number of *s
code of the top of my head, may not compile but try:
Regex r = new Regex(#"\*&");
Regex r2 = new Regex(#"\*");
if (r.Matches(myString).Count == r2.Matches(myString).Count) //success!
What is the best (shortest and fastest) way to check if StringBuilder ends with specific string?
If I want to check just one char, that's not a problem sb[sb.Length-1] == 'c', but how to check if it's ends with longer string?
I can think about something like looping from "some string".Length and read characters one by one, but maybe there exists something more simple? :)
At the end I want to have extension method like this:
StringBuilder sb = new StringBuilder("Hello world");
bool hasString = sb.EndsWith("world");
To avoid the performance overhead of generating the full string, you can use the ToString(int,int) overload that takes the index range.
public static bool EndsWith(this StringBuilder sb, string test)
{
if (sb.Length < test.Length)
return false;
string end = sb.ToString(sb.Length - test.Length, test.Length);
return end.Equals(test);
}
Edit: It would probably be desirable to define an overload that takes a StringComparison argument:
public static bool EndsWith(this StringBuilder sb, string test)
{
return EndsWith(sb, test, StringComparison.CurrentCulture);
}
public static bool EndsWith(this StringBuilder sb, string test,
StringComparison comparison)
{
if (sb.Length < test.Length)
return false;
string end = sb.ToString(sb.Length - test.Length, test.Length);
return end.Equals(test, comparison);
}
Edit2: As pointed out by Tim S in the comments, there is a flaw in my answer (and all other answers that assume character-based equality) that affects certain Unicode comparisons. Unicode does not require two (sub)strings to have the same sequence of characters to be considered equal. For example, the precomposed character é should be treated as equal to the character e followed by the combining mark U+0301.
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
string s = "We met at the cafe\u0301";
Console.WriteLine(s.EndsWith("café")); // True
StringBuilder sb = new StringBuilder(s);
Console.WriteLine(sb.EndsWith("café")); // False
If you want to handle these cases correctly, it might be easiest to just call StringBuilder.ToString(), and then use the built-in String.EndsWith.
On msdn you can find the topic on how to search text in the StringBuilder object. The two options available to you are:
Call ToString and search the returned String object.
Use the Chars property to sequentially search a range of characters.
Since the first option is out of the question. You'll have to go with the Chars property.
public static class StringBuilderExtensions
{
public static bool EndsWith(this StringBuilder sb, string text)
{
if (sb.Length < text.Length)
return false;
var sbLength = sb.Length;
var textLength = text.Length;
for (int i = 1; i <= textLength; i++)
{
if (text[textLength - i] != sb[sbLength - i])
return false;
}
return true;
}
}
TL;DR
If you're goal is to get a piece or the whole of the StringBuilder's contents in a String object, you should use its ToString function. But if you aren't yet done creating your string, it's better to treat the StringBuilder as a character array and operate in that way than to create a bunch of strings you don't need.
String operations on a character array can become complicated by localization or encoding, since a string can be encoded in many ways (UTF8 or Unicode, for example), but its characters (System.Char) are meant to be 16-bit UTF16 values.
I've written the following method which returns the index of a string if it exists within the StringBuilder and -1 otherwise. You can use this to create the other common String methods like Contains, StartsWith, and EndsWith. This method is preferable to others because it should handle localization and casing properly, and does not force you to call ToString on the StringBuilder. It creates one garbage value if you specify that case should be ignored, and you can fix this to maximize memory savings by using Char.ToLower instead of precomputing the lower case of the string like I do in the function below. EDIT: Also, if you're working with a string encoded in UTF32, you'll have to compare two characters at a time instead of just one.
You're probably better off using ToString unless you're going to be looping, working with large strings, and doing manipulation or formatting.
public static int IndexOf(this StringBuilder stringBuilder, string str, int startIndex = 0, int? count = null, CultureInfo culture = null, bool ignoreCase = false)
{
if (stringBuilder == null)
throw new ArgumentNullException("stringBuilder");
// No string to find.
if (str == null)
throw new ArgumentNullException("str");
if (str.Length == 0)
return -1;
// Make sure the start index is valid.
if (startIndex < 0 && startIndex < stringBuilder.Length)
throw new ArgumentOutOfRangeException("startIndex", startIndex, "The index must refer to a character within the string.");
// Now that we've validated the parameters, let's figure out how many characters there are to search.
var maxPositions = stringBuilder.Length - str.Length - startIndex;
if (maxPositions <= 0) return -1;
// If a count argument was supplied, make sure it's within range.
if (count.HasValue && (count <= 0 || count > maxPositions))
throw new ArgumentOutOfRangeException("count");
// Ensure that "count" has a value.
maxPositions = count ?? maxPositions;
if (count <= 0) return -1;
// If no culture is specified, use the current culture. This is how the string functions behave but
// in the case that we're working with a StringBuilder, we probably should default to Ordinal.
culture = culture ?? CultureInfo.CurrentCulture;
// If we're ignoring case, we need all the characters to be in culture-specific
// lower case for when we compare to the StringBuilder.
if (ignoreCase) str = str.ToLower(culture);
// Where the actual work gets done. Iterate through the string one character at a time.
for (int y = 0, x = startIndex, endIndex = startIndex + maxPositions; x <= endIndex; x++, y = 0)
{
// y is set to 0 at the beginning of the loop, and it is increased when we match the characters
// with the string we're searching for.
while (y < str.Length && str[y] == (ignoreCase ? Char.ToLower(str[x + y]) : str[x + y]))
y++;
// The while loop will stop early if the characters don't match. If it didn't stop
// early, that means we found a match, so we return the index of where we found the
// match.
if (y == str.Length)
return x;
}
// No matches.
return -1;
}
The primary reason one generally uses a StringBuilder object rather than concatenating strings is because of the memory overhead you incur since strings are immutable. The performance hit you see when you do excessive string manipulation without using a StringBuilder is often the result of collecting all the garbage strings you created along the way.
Take this for example:
string firstString = "1st",
secondString = "2nd",
thirdString = "3rd",
fourthString = "4th";
string all = firstString;
all += " & " + secondString;
all += " &" + thirdString;
all += "& " + fourthString + ".";
If you were to run this and open it up in a memory profiler, you'd find a set of strings that look something like this:
"1st", "2nd", "3rd", "4th",
" & ", " & 2nd", "1st & 2nd"
" &", "&3rd", "1st & 2nd &3rd"
"& ", "& 4th", "& 4th."
"1st & 2nd &3rd& 4th."
That's fourteen total objects we created in that scope, but if you don't realize that every single addition operator creates a whole new string every time you might think there's only five. So what happens to the nine other strings? They languish away in memory until the garbage collector decides to pick them up.
So now to my point: if you're trying to find something out about a StringBuilder object and you're not wanting to call ToString(), it probably means you aren't done building that string yet. And if you're trying to find out if the builder ends with "Foo", it's wasteful to call sb.ToString(sb.Length - 1, 3) == "Foo" because you're creating another string object that becomes orphaned and obsolete the minute you made the call.
My guess is that you're running a loop aggregating text into your StringBuilder and you want to end the loop or just do something different if the last few characters are some sentinel value you're expecting.
private static bool EndsWith(this StringBuilder builder, string value) {
return builder.GetLast( value.Length ).SequenceEqual( value );
}
private static IEnumerable<char> GetLast(this StringBuilder builder, int count) {
count = Math.Min( count, builder.Length );
return Enumerable.Range( builder.Length - count, count ).Select( i => builder[ i ] );
}
I'm giving you what you asked for (with the limitations you state) but not the best way to do it. Something like:
StringBuilder sb = new StringBuilder("Hello world");
bool hasString = sb.Remove(1,sb.Length - "world".Length) == "world";