I am trying to write a function to control if the parenthesis included in a stringare balanced or not.
I have written the following function:
public bool IsBalanced(string input)
{
//last condition, gets out
if (string.IsNullOrEmpty(input)) return true;
int numOpen = 0;
bool opened = false;
foreach (char c in input)
{
if (char.ToUpperInvariant(c) =='(')
{
opened = true;
numOpen+=1;
}
if (char.ToUpperInvariant(c) == ')')
{
if (opened)
{
numOpen-=1;
opened = numOpen > 0;
}
else
return false; //incorrect position parentheses, closes but not opened
}
}
return numOpen == 0;
}
I wanted to change it to a recursive function, but have not been able to do so. Could anyone give a hint how to do it?
Well, your algorithm is not pretty. Here is a better one
int check = 0;
foreach (var c in input)
{
if (c == '(') {
check++;
} else if (c == ')') {
check--;
}
if (check < 0) {
return false; // error, closing bracket without opening brackets first
}
}
return check == 0; // > 0 error, missing some closing brackets
To make it (algorithm of checking for balanced brackets) recursive you can go with following
bool IsBalanced(string input)
{
var first = input.IndexOf('('); // first opening bracket position
var last = input.LastIndexOf(')'); // last closing bracket position
if (first == -1 && last == -1)
return true; // no more brackets - balanced
if (first == -1 && last != -1 || first != -1 && last == -1)
return false; // error - one of brackets is missing
if (first > last)
return false; // error - closing bracket is BEFORE opening
return IsBalanced(input.Substring(first, last - first)); // not sure, might need to tweak it, parameter should be without first and last bracket (what is inside)
}
This simply remove first opening brackets and last closing bracket and pass what is left as parameter (recursively) until one of the end conditions is met.
The basic idea is to take a variant (numOpen in this case) as an argument.
Here is my code:
public bool IsBalancedRec(string input, int numOpen = 0)
{
if (numOpen < 0)
return false;
if (string.IsNullOrEmpty(input))
return numOpen == 0;
char c = input[0];
string rest = input.Substring(1);
if (c == '(')
return IsBalancedRec(rest, numOpen + 1);
else if (c == ')')
return IsBalancedRec(rest, numOpen - 1);
else
return IsBalancedRec(rest, numOpen);
}
And call this like IsBalancedRec("so(m(eth)ing)").
Implement with stack:
Stack myStak = new Stack();
public bool IsBalanced(string input)
{
if (input.ToArray().Count() != 0)
{
if(input.ToArray()[0] == '(')
{
myStak.Push('(');
}
else if(input.ToArray()[0] == ')')
{
if (myStak.Count != 0)
myStak.Pop();
else
{
//not balanced
return false;
}
}
return IsBalanced(input.Substring(1));
}
else
{
if (myStak.Count == 0)
{
//balanced
return true;
}
else
{
//not balanced
return false;
}
}
}
public static bool IsBalanced(string input)
{
int numOpen = 0;
while(input != "")
{
char c = input[0];
input = input.Substring(1);
numOpen = c == '(' ? (numOpen + 1) : (c == ')' ? (numOpen - 1) : numOpen);
}
return numOpen == 0;
}
// count no of left and right bracket or parenthesis and check counts
var InputStr= str.ToCharArray();
int left = 0;
int right = 0;
foreach (char item in InputStr)
{
if(item == '(')
{
left ++;
} else if(item == ')')
{
right ++;
}
}
if(right == l)
{
return "1";
}
return "0";
}
Related
I have coded a function that check if brackets in a certain string are valid and returns true if it is and false if it isn't.
For example:
str1: { [ a + b ] - ] ( c - d } ] = false.
str2: { [ a + b ] - ( c - d ) } = true.
When I run the program it doesn't give any output, just a blank output.
What do I need to change?
public static Boolean BracketCheck(string str)
{
Stack<char> stk = new Stack<char>();
Stack<char> aid = new Stack<char>();
Stack<char> temp = new Stack<char>();
while (str != "")
{
char ch = str[0];
if(ch == '(' || ch == '{' || ch == '[' || ch == ')' || ch == '}' || ch == ']')
{
stk.Push(ch);
}
if(str.Length != 1)
str = str.Substring(1, str.Length - 1);
}
stk = Opposite(stk);
char first = stk.Pop();
char last;
while (!stk.IsEmpty() && !aid.IsEmpty())
{
while (!stk.IsEmpty())
{
aid.Push(stk.Top());
last = stk.Pop();
if (stk.IsEmpty())
if (int.Parse(first + "") + 1 != int.Parse(last + "") || int.Parse(first + "") + 2 != int.Parse(last + ""))
{
return false;
}
}
first = aid.Pop();
while (!aid.IsEmpty())
{
aid.Push(aid.Top());
last = aid.Pop();
if (aid.IsEmpty())
if (int.Parse(first + "") + 1 != int.Parse(last + "") || int.Parse(first + "") + 2 != int.Parse(last + ""))
{
return false;
}
}
first = stk.Pop();
}
return true;
}
public static Stack<char> Opposite(Stack<char> stk)
{
Stack<char> temp = new Stack<char>();
while (stk.IsEmpty())
{
temp.Push(stk.Pop());
}
return temp;
}
You are on the right way (Stack) but it should be just one, not three. To check brackets validity only:
public static Boolean BracketCheck(string str) {
if (string.IsNullOrEmpty(str))
return true;
Stack<char> expected = new Stack<char>();
foreach (char c in str) {
if (c == '(')
expected.Push(')');
else if (c == '[')
expected.Push(']');
else if (c == '{')
expected.Push('}');
else if (c == ')' || c == ']' || c == '}') {
if (expected.Count == 0 || expected.Pop() != c)
return false;
}
}
return expected.Count == 0;
}
If you want to validate the string as a formula, e.g. (3 +) 5 has valid brackets, but is invalid formula, have a look at shunting yard algorithm
You created aid and did nothing with it before the line while (!stk.IsEmpty() && !aid.IsEmpty()) so aid is empty and nothing in that loop ever runs.
There's also a bunch of things that might be better asked on the code review site; for example you don't need to remove characters from a string to iterate over the characters in it or convert chars to strings to integers to compare them.
Essentially what you want to do is create a stack, iterate over the string, any opening bracket push to the stack, any closing bracket pop the stack and check the opening bracket matches, and if at the end of the string the stack is empty then it is valid. You don't need all the reversing and creating second stack stuff.
this works for me do you see any issuse or points to improve on?
public static Boolean BracketCheck(string str)
{
Stack<char> stk = new Stack<char>();
foreach(char c in str)
{
if (c == '(' || c == '[' || c == '{')
{
stk.Push(c);
}
else if (c == ')' || c == ']' || c == '}')
{
if (stk.Top() == (int)c - 1 || stk.Top() == (int)c - 2)
{
stk.Pop();
}
}
}
return stk.IsEmpty();
}
Suppose I had a FormattableString like so:
var now = DateTime.Now;
FormattableString str = $"Today's date is: {now:yyyy-MM-dd} and some numbers: {new[]{1,2,3}}";
I'm trying to take a formattable string and transform it to be used in another component. In my case, essentially splitting the format string at the values so I can control how they're concatenated back, but I need the format string for the argument.
There are some limited methods on the FormattableString that allows me to get the argument values (GetArguments()/GetArgument()) and the original format string (Format), but there is none for accessing the argument formatting strings.
var format = str.Format; // "Today's date is: {0:yyyy-MM-dd} and some numbers: {1}"
var arguments = str.GetArguments(); // { now, new[]{1,2,3} }
// no simple way to get the "yyyy-MM-dd" part for arg 0
My workarounds I'm looking at are to preformat the value so the formatted value is set as the argument or parsing out the format string which would not be ideal.
void DumpLine(FormattableString format)
{
var values = format.GetArguments().Prepend(null);
var parts = Regex.Split(format.Format, #"\{[^}]+\}");
Util.HorizontalRun(false, values.Zip(parts).SelectMany(x => new[] { x.First, x.Second }).Skip(1)).Dump();
}
// usage
DumpLine($"Today's date is: {now.ToString("yyyy-MM-dd")} and some numbers: {new[]{1,2,3}}");
Is this supported, perhaps through a helper class or am I out of luck?
Thank you mariusz96 for pointing out the new InterpolatedStringHandler functionality. It does for me exactly what I needed it for. It's even flexible enough for me to add additional parameters should I need it in the future. This is what I ended up with:
public static class UtilEx
{
public static object Interpolate(IFormatProvider provider, [InterpolatedStringHandlerArgument(nameof(provider))] InterpolateFormatHandler handler) => Interpolate(handler);
public static object Interpolate(InterpolateFormatHandler handler) => Util.HorizontalRun(false, handler.Items);
[InterpolatedStringHandler]
public ref struct InterpolateFormatHandler
{
private readonly IFormatProvider? provider;
public InterpolateFormatHandler(int _literalLength, int _formattedCount, IFormatProvider? provider = default) => this.provider = provider;
public List<object?> Items { get; } = new();
public void AppendLiteral(string s) => Items.Add(s);
public void AppendFormatted<T>(T t, int alignment, string format) => Items.Add(string.Format(provider, $"{{0,{alignment}:{format}}}", t));
public void AppendFormatted<T>(T t, int alignment) => Items.Add(string.Format(provider, $"{{0,{alignment}}}", t));
public void AppendFormatted<T>(T t, string format) => Items.Add(string.Format(provider, $"{{0:{format}}}", t));
public void AppendFormatted<T>(T t) => Items.Add(t);
}
}
This is possible with a custom InterpolatedStringHandler.
It has overloads that take alignment and format.
none for accessing the argument formatting strings.. Is this supported, perhaps through a helper class or am I out of luck?
Interesting question. I'm tempted to say "no" - though I don't have any references/sources to cite I've always regarded FormattableString as a helper for interpolation that enables some parts of a program to know the difference between contexts where it receives a string, and contexts where it receives a formattable string - in essence, to know that something was once a string with format placeholders is helpful in cases like an SQL ORM running a raw command, and wanting to parameterize it. If it receives a FormattableString, it can parameterize the arguments and know where to insert them by parsing the format. If it straight received a formatted string it wouldn't be able to do that, so a FormattableString allows us to keep the format string and related arguments separated until the last moment.
When the compiler is turning an interpolated string into a formattable one, it has a relatively easy task. Take a look at what happens in this simple example, run through sharplab's compile/decompile cycle:
It's essentially just collecting variables mentioned inside a string, numbering them and swapping the interp out for a standard numerical placeholder format string. It doesn't need to touch the formatting specifiers when it converts {b:0000} -> {0:0000}
So the actual formats embedded in the placeholders aren't separated out at this stage; they're parsed out later. If we take a look at this internal method of a stringbuilder, which is what string.Format (eventually) defers to (which is what formattable string defers to), we can see it operating in statemachine parser style, hunting for non escaped {, parsing the numeric holder number, and then pulling the format and padding specifiers out by reference to commas and colons:
//from .net framework reference source
internal StringBuilder AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args) {
if (format == null) {
throw new ArgumentNullException("format");
}
Contract.Ensures(Contract.Result<StringBuilder>() != null);
Contract.EndContractBlock();
int pos = 0;
int len = format.Length;
char ch = '\x0';
ICustomFormatter cf = null;
if (provider != null) {
cf = (ICustomFormatter)provider.GetFormat(typeof(ICustomFormatter));
}
while (true) {
int p = pos;
int i = pos;
while (pos < len) {
ch = format[pos];
pos++;
if (ch == '}')
{
if (pos < len && format[pos] == '}') // Treat as escape character for }}
pos++;
else
FormatError();
}
if (ch == '{')
{
if (pos < len && format[pos] == '{') // Treat as escape character for {{
pos++;
else
{
pos--;
break;
}
}
Append(ch);
}
if (pos == len) break;
pos++;
if (pos == len || (ch = format[pos]) < '0' || ch > '9') FormatError();
int index = 0;
do {
index = index * 10 + ch - '0';
pos++;
if (pos == len) FormatError();
ch = format[pos];
} while (ch >= '0' && ch <= '9' && index < 1000000);
if (index >= args.Length) throw new FormatException(Environment.GetResourceString("Format_IndexOutOfRange"));
while (pos < len && (ch = format[pos]) == ' ') pos++;
bool leftJustify = false;
int width = 0;
if (ch == ',') {
pos++;
while (pos < len && format[pos] == ' ') pos++;
if (pos == len) FormatError();
ch = format[pos];
if (ch == '-') {
leftJustify = true;
pos++;
if (pos == len) FormatError();
ch = format[pos];
}
if (ch < '0' || ch > '9') FormatError();
do {
width = width * 10 + ch - '0';
pos++;
if (pos == len) FormatError();
ch = format[pos];
} while (ch >= '0' && ch <= '9' && width < 1000000);
}
while (pos < len && (ch = format[pos]) == ' ') pos++;
Object arg = args[index];
StringBuilder fmt = null;
if (ch == ':') {
pos++;
p = pos;
i = pos;
while (true) {
if (pos == len) FormatError();
ch = format[pos];
pos++;
if (ch == '{')
{
if (pos < len && format[pos] == '{') // Treat as escape character for {{
pos++;
else
FormatError();
}
else if (ch == '}')
{
if (pos < len && format[pos] == '}') // Treat as escape character for }}
pos++;
else
{
pos--;
break;
}
}
if (fmt == null) {
fmt = new StringBuilder();
}
fmt.Append(ch);
}
}
if (ch != '}') FormatError();
pos++;
String sFmt = null;
String s = null;
if (cf != null) {
if (fmt != null) {
sFmt = fmt.ToString();
}
s = cf.Format(sFmt, arg, provider);
}
if (s == null) {
IFormattable formattableArg = arg as IFormattable;
#if FEATURE_LEGACYNETCF
if(CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
// TimeSpan does not implement IFormattable in Mango
if(arg is TimeSpan) {
formattableArg = null;
}
}
#endif
if (formattableArg != null) {
if (sFmt == null && fmt != null) {
sFmt = fmt.ToString();
}
s = formattableArg.ToString(sFmt, provider);
} else if (arg != null) {
s = arg.ToString();
}
}
if (s == null) s = String.Empty;
int pad = width - s.Length;
if (!leftJustify && pad > 0) Append(' ', pad);
Append(s);
if (leftJustify && pad > 0) Append(' ', pad);
}
return this;
}
https://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs,2c3b4c2e7c43f5a4
All in, I'd say if you want those format arguments you'll need to pull them from the Format yourself; it's what the framework does. The code that does it is above, and could be tarted up/slimmed down but if you adopt the same approach with a helper as the framework does it should be similarly reliable and consistent in its behavior
ps; here's the source for the same method from .net core; essentially the same, but more nicely commented ;)
internal StringBuilder AppendFormatHelper(IFormatProvider? provider, string format, ParamsArray args)
{
if (format == null)
{
throw new ArgumentNullException(nameof(format));
}
int pos = 0;
int len = format.Length;
char ch = '\x0';
ICustomFormatter? cf = null;
if (provider != null)
{
cf = (ICustomFormatter?)provider.GetFormat(typeof(ICustomFormatter));
}
while (true)
{
while (pos < len)
{
ch = format[pos];
pos++;
// Is it a closing brace?
if (ch == '}')
{
// Check next character (if there is one) to see if it is escaped. eg }}
if (pos < len && format[pos] == '}')
{
pos++;
}
else
{
// Otherwise treat it as an error (Mismatched closing brace)
FormatError();
}
}
// Is it an opening brace?
else if (ch == '{')
{
// Check next character (if there is one) to see if it is escaped. eg {{
if (pos < len && format[pos] == '{')
{
pos++;
}
else
{
// Otherwise treat it as the opening brace of an Argument Hole.
pos--;
break;
}
}
// If it's neither then treat the character as just text.
Append(ch);
}
//
// Start of parsing of Argument Hole.
// Argument Hole ::= { Index (, WS* Alignment WS*)? (: Formatting)? }
//
if (pos == len)
{
break;
}
//
// Start of parsing required Index parameter.
// Index ::= ('0'-'9')+ WS*
//
pos++;
// If reached end of text then error (Unexpected end of text)
// or character is not a digit then error (Unexpected Character)
if (pos == len || (ch = format[pos]) < '0' || ch > '9') FormatError();
int index = 0;
do
{
index = index * 10 + ch - '0';
pos++;
// If reached end of text then error (Unexpected end of text)
if (pos == len)
{
FormatError();
}
ch = format[pos];
// so long as character is digit and value of the index is less than 1000000 ( index limit )
}
while (ch >= '0' && ch <= '9' && index < IndexLimit);
// If value of index is not within the range of the arguments passed in then error (Index out of range)
if (index >= args.Length)
{
throw new FormatException(SR.Format_IndexOutOfRange);
}
// Consume optional whitespace.
while (pos < len && (ch = format[pos]) == ' ') pos++;
// End of parsing index parameter.
//
// Start of parsing of optional Alignment
// Alignment ::= comma WS* minus? ('0'-'9')+ WS*
//
bool leftJustify = false;
int width = 0;
// Is the character a comma, which indicates the start of alignment parameter.
if (ch == ',')
{
pos++;
// Consume Optional whitespace
while (pos < len && format[pos] == ' ') pos++;
// If reached the end of the text then error (Unexpected end of text)
if (pos == len)
{
FormatError();
}
// Is there a minus sign?
ch = format[pos];
if (ch == '-')
{
// Yes, then alignment is left justified.
leftJustify = true;
pos++;
// If reached end of text then error (Unexpected end of text)
if (pos == len)
{
FormatError();
}
ch = format[pos];
}
// If current character is not a digit then error (Unexpected character)
if (ch < '0' || ch > '9')
{
FormatError();
}
// Parse alignment digits.
do
{
width = width * 10 + ch - '0';
pos++;
// If reached end of text then error. (Unexpected end of text)
if (pos == len)
{
FormatError();
}
ch = format[pos];
// So long a current character is a digit and the value of width is less than 100000 ( width limit )
}
while (ch >= '0' && ch <= '9' && width < WidthLimit);
// end of parsing Argument Alignment
}
// Consume optional whitespace
while (pos < len && (ch = format[pos]) == ' ') pos++;
//
// Start of parsing of optional formatting parameter.
//
object? arg = args[index];
ReadOnlySpan<char> itemFormatSpan = default; // used if itemFormat is null
// Is current character a colon? which indicates start of formatting parameter.
if (ch == ':')
{
pos++;
int startPos = pos;
while (true)
{
// If reached end of text then error. (Unexpected end of text)
if (pos == len)
{
FormatError();
}
ch = format[pos];
if (ch == '}')
{
// Argument hole closed
break;
}
else if (ch == '{')
{
// Braces inside the argument hole are not supported
FormatError();
}
pos++;
}
if (pos > startPos)
{
itemFormatSpan = format.AsSpan(startPos, pos - startPos);
}
}
else if (ch != '}')
{
// Unexpected character
FormatError();
}
// Construct the output for this arg hole.
pos++;
string? s = null;
string? itemFormat = null;
if (cf != null)
{
if (itemFormatSpan.Length != 0)
{
itemFormat = new string(itemFormatSpan);
}
s = cf.Format(itemFormat, arg, provider);
}
if (s == null)
{
// If arg is ISpanFormattable and the beginning doesn't need padding,
// try formatting it into the remaining current chunk.
if (arg is ISpanFormattable spanFormattableArg &&
(leftJustify || width == 0) &&
spanFormattableArg.TryFormat(RemainingCurrentChunk, out int charsWritten, itemFormatSpan, provider))
{
if ((uint)charsWritten > (uint)RemainingCurrentChunk.Length)
{
// Untrusted ISpanFormattable implementations might return an erroneous charsWritten value,
// and m_ChunkLength might end up being used in Unsafe code, so fail if we get back an
// out-of-range charsWritten value.
FormatError();
}
m_ChunkLength += charsWritten;
// Pad the end, if needed.
int padding = width - charsWritten;
if (leftJustify && padding > 0)
{
Append(' ', padding);
}
// Continue to parse other characters.
continue;
}
// Otherwise, fallback to trying IFormattable or calling ToString.
if (arg is IFormattable formattableArg)
{
if (itemFormatSpan.Length != 0)
{
itemFormat ??= new string(itemFormatSpan);
}
s = formattableArg.ToString(itemFormat, provider);
}
else if (arg != null)
{
s = arg.ToString();
}
}
// Append it to the final output of the Format String.
if (s == null)
{
s = string.Empty;
}
int pad = width - s.Length;
if (!leftJustify && pad > 0)
{
Append(' ', pad);
}
Append(s);
if (leftJustify && pad > 0)
{
Append(' ', pad);
}
// Continue to parse other characters.
}
return this;
}
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs
I need to write a function which verifies parentheses are balanced in a string. Each open parentheses should have a corresponding close parentheses and they should correspond correctly.
For example, the function should return true for the following strings:
(if (any? x) sum (/1 x))
I said (it's not (yet) complete). (she didn't listen)
The function should return false for the following strings:
:-)
())(
OPTIONAL BONUS
Implement the solution as a recursive function with no mutation / side-effects.
Can you please help me out in writing using C# because I'm new to .NET technologies.
Thanks.
Here's what I've tried so far. It works for sending parameters as open and close brackets, but I'm confused with just passing a string... and also I should not use stack.
private static bool Balanced(string input, string openParenthesis, string closedParenthesis)
{
try
{
if (input.Length > 0)
{
//Obtain first character
string firstString = input.Substring(0, 1);
//Check if it is open parenthesis
//If it is open parenthesis push it to stack
//If it is closed parenthesis pop it
if (firstString == openParenthesis)
stack.Push(firstString);
else if (firstString == closedParenthesis)
stack.Pop();
//In next iteration, chop off first string so that it can iterate recursively through rest of the string
input = input.Substring(1, input.Length - 1);
Balanced(input, openParenthesis, closedParenthesis); //this call makes the function recursive
}
if (stack.Count == 0 && !exception)
isBalanced = true;
}
catch (Exception ex)
{
exception = true;
}
return isBalanced;
}
You don't need to use any recursion method for such a simple requirement, just try this simple method and it works like a charm:
public bool AreParenthesesBalanced(string input) {
int k = 0;
for (int i = 0; i < input.Length; i++) {
if (input[i] == '(') k++;
else if (input[i] == ')'){
if(k > 0) k--;
else return false;
}
}
return k == 0;
}
I have used the startIndex and increment with each recursive call
List<string> likeStack = new List<string>();
private static bool Balanced(string input, string openParenthesis, string closedParenthesis , int startIndex)
{
try
{
if (startIndex < input.Length)
{
//Obtain first character
string firstString = input.Substring(startIndex, 1);
//Check if it is open parenthesis
//If it is open parenthesis push it to stack
//If it is closed parenthesis pop it
if (firstString == openParenthesis)
likeStack.Add(firstString);
else if (firstString == closedParenthesis)
likeStack.RemoveAt(likeStack.Count -1);
//In next iteration, chop off first string so that it can iterate recursively through rest of the string
Balanced(input, openParenthesis, closedParenthesis , startIndex + 1); //this call makes the function recursive
}
if (likeStack.Count == 0 && !exception)
isBalanced = true;
}
catch (Exception ex)
{
exception = true;
}
return isBalanced;
}
How's this recursive version?
public static bool Balanced(string s)
{
var ix = -1;
return Balanced(s, false, ref ix);
}
private static bool Balanced(string s, bool inParens, ref int ix)
{
ix++;
while (ix < s.Length)
{
switch (s[ix++])
{
case '(':
if (!Balanced(s, true, ref ix))
return false;
break;
case ')':
return inParens;
}
}
return !inParens;
}
my program should print a message on the screen if the formula that the user entered is good for the terms(you can only use digits and letters, u can't start with '(' and like mathematical formula, for each open bracket, has to be a suitable(and in the right place) close bracket.
here some formulas that the program should accepts and prints:
True-
a(aa(a)aaa(aa(a)aa)aa)aaaaa
a(((())))
here some formulas that the program should not accepts and prints:
False-
()()()
)()()(
but the program always prints False
thanks for helping
Heres the code:EDIT
bool IsNumeric(char character)
{
return "0123456789".Contains(character);
// or return Char.IsNumber(character);
}
bool IsLetter(char character)
{
return "ABCDEFGHIJKLMNOPQRSTUVWXWZabcdefghigjklmnopqrstuvwxyz".Contains(character);
}
bool IsRecognized(char character)
{
return IsBracket(character) | IsNumeric(character) | IsLetter(character);
}
public bool IsValidInput(string input)
{
if (String.IsNullOrEmpty(input) || IsBracket(input[0]))
{
return false;
}
var bracketsCounter = 0;
for (var i = 0; i < input.Length; i++)
{
var character = input[i];
if (!IsRecognized(character))
{
return false;
}
if (IsBracket(character))
{
if (character == '(')
bracketsCounter++;
if (character == ')')
bracketsCounter--;
}
}
if (bracketsCounter > 0)
{
return false;
}
return bracketsCounter==0;
}
}
}
Your algorithm is unnecessarily complex - all you need is a loop and a counter.
Check the initial character for ( (you already do that)
Set the counter to zero, and go through each character one by one
If the character is not a letter or a parentheses, return false
If the character is an opening (, increment the counter
If the character is a closing ), decrement the counter; if the counter is less than zero, return false
Return true if the count is zero after the loop has ended; otherwise return false
Is debugging this hard really? This condition:
((!IsNumeric(st[i])) && (st[i] != '(') && (st[i] != ')')&&((st[i]<'a')||(st[i]>'z')||(st[i]<'A')||(st[i]>'Z')))
return false;
is obviously wrong. It returns false for a every time. You don't take into consideration that a is greater than Z.
EDIT:
so how can i make it easier to read? that the only way i figured. do u
have other solution for this problem?
As for that condition block - use smaller methods / functions, for example.
bool IsBracket(char character)
{
return (character == '(' | character == ')');
}
bool IsNumeric(char character)
{
return "0123456789".Contains(character);
// or return Char.IsNumber(character);
}
bool IsLetter(char character)
{
// see why this is NOT prone to fail just because 'a' is greater than 'Z' in C#?
return (character >= 'a' & character <= 'z') |
(character >= 'A' & character <= 'Z');
// or return Regex.IsMatch(character.ToString(), "[a-zA-Z]", RegexOptions.None);
// or return Char.IsLetter(character);
}
// now you can implement:
bool IsRecognized(char character)
{
return IsBracket(character) | IsNumeric(character) | IsLetter(character);
}
and then in your big method you could just safely use:
if (!IsRecognized(st[i]))
return false;
It may look like an overkill for such a trivial example, but it's a better approach in principle, and certainly more readable.
And after that, you could reduce your code to something along the lines of:
bool IsInputValid(string input)
{
if (String.IsNullOrEmpty(input) || IsBracket(input[0]))
{
return false;
}
var bracketsCounter = 0;
for (var i = 0; i < input.Length; i++)
{
var character = input[i];
if (!IsRecognized(character))
{
return false;
}
if (IsBracket(character)) // redundant?
{
if (character == '(') // then what?
if (character == ')') // then what?
}
if (bracketsCounter < what?)
{
what?
}
}
return bracketsCounter == what?;
}
(dasblinkenlight's algorithm)
EDIT 10th April
You got it wrong.
bool IsNumeric(char character)
{
return "0123456789".Contains(character);
// or return Char.IsNumber(character);
}
bool IsLetter(char character)
{
return "ABCDEFGHIJKLMNOPQRSTUVWXWZabcdefghigjklmnopqrstuvwxyz".Contains(character);
}
bool IsRecognized(char character)
{
return IsBracket(character) | IsNumeric(character) | IsLetter(character);
}
public bool IsValidInput(string input)
{
if (String.IsNullOrEmpty(input) || IsBracket(input[0]))
{
return false;
}
var bracketsCounter = 0;
for (var i = 0; i < input.Length; i++)
{
var character = input[i];
if (!IsRecognized(character))
{
return false;
}
if (IsBracket(character))
{
if (character == '(')
bracketsCounter++;
if (character == ')')
bracketsCounter--;
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (bracketsCounter < 0) // NOT "> 0", and HERE - INSIDE the for loop
{
return false;
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
return bracketsCounter==0;
}
}
}
By the way, you also made a mistake in your IsLetter method:
...UVWXWZ? Should be UVWXYZ
Is there any way to detect both Readline and ReadKey, so that in most cases it behaves as a readline, except for some special key inputs that should be detected?
I need some "parallel" implementation to introduce simultaneity.
The code below is synchronous and does not meet my need
while ((line = Console.ReadLine()) != "x")
{
if (line == "BLABLA")
{
//Stuff
}
else
{
//Stuff
}
ConsoleKeyInfo ki = Console.ReadKey(true);
if ((ki.Key == ConsoleKey.V) && (ki.Modifiers == ConsoleModifiers.Control))
{
//Stuff
}
}
Here's a function I just made to do this.
Right now it only handles Backspace, Enter and Esc, but it could easily be modified to handle other keys if you deem them necessary.
// returns null if user pressed Escape, or the contents of the line if they pressed Enter.
private static string ReadLineOrEsc()
{
string retString = "";
int curIndex = 0;
do
{
ConsoleKeyInfo readKeyResult = Console.ReadKey(true);
// handle Esc
if (readKeyResult.Key == ConsoleKey.Escape)
{
Console.WriteLine();
return null;
}
// handle Enter
if (readKeyResult.Key == ConsoleKey.Enter)
{
Console.WriteLine();
return retString;
}
// handle backspace
if (readKeyResult.Key == ConsoleKey.Backspace)
{
if (curIndex > 0)
{
retString = retString.Remove(retString.Length - 1);
Console.Write(readKeyResult.KeyChar);
Console.Write(' ');
Console.Write(readKeyResult.KeyChar);
curIndex--;
}
}
else
// handle all other keypresses
{
retString += readKeyResult.KeyChar;
Console.Write(readKeyResult.KeyChar);
curIndex++;
}
}
while (true);
}
No, not as such. Both methods block until the user enters something on the console. So even if you would find a way to have both run in parallel, it will not be deterministic which one gets the first shot.
There is a (not obvious) similar problem: how to make Console.ReadLine() abort/break after a certain amount of time without user input.
There have been multiple attempts for this problem here:
Console.ReadLine Break
How to add a Timeout to Console.ReadLine()?
Most are modelled around either creating your own version of a ReadLine function that adds a timeout (or in your case special handling for certain character (codes)) or the use some sort of threading.
Both ways are either non-trivial or have their own issues (make sure you review the comments, even for the accepted answers).
In short, I think you will need to roll your own version of ReadLine, based on Console.ReadKey with your special handling included and that much of the genuine Console.ReadLine behavior that you require. Note that this even include such basic things as RETURN, ARROW KEYS, BACKSPACE handling, etc.
Update: There is the getline.cs Code from the Mono project, which implements a line editing capability like it was provided by some venerable UNIX shells (EMACS mode, if you care). For that, I believe it will need to implement some sort of ReadLine replacement, although I haven't checked. Maybe you can use this as a starting point.
Here is a method that I created that works great. You do not have to press button twice in order for string to start appearing. Basically this replaces Console.ReadLine() but it also looks for Esc key pressed. Just look at return type of method if it is null then you know Esc was pressed.
private string ReadLineOrEscape()
{
ConsoleKeyInfo keyInfo = new ConsoleKeyInfo();
StringBuilder sb = new StringBuilder();
int index = 0;
while (keyInfo.Key != ConsoleKey.Enter)
{
keyInfo = Console.ReadKey(true);
if (keyInfo.Key == ConsoleKey.Escape)
{
return null;
}
if(keyInfo.Key == ConsoleKey.Backspace)
{
if (index > 0)
{
Console.CursorLeft = index - 1;
sb.Remove(index - 1, 1);
Console.Write(" \b");
index--;
}
}
if(keyInfo.KeyChar > 31 && keyInfo.KeyChar < 127)
{
index++;
Console.Write(keyInfo.KeyChar);
sb.Append(keyInfo.KeyChar);
}
}
return sb.ToString(); ;
}
In response to #Overlord Zurd, I improved the code provided by the user.
public class ConsoleOutput
{
private ConsoleOutputType OutputType { get; set; }
private object MyObject { get; }
private static bool IsInserting { get; set; }
public string KeyName => IsKey() && (ConsoleKeyInfo)MyObject != null ? ((ConsoleKeyInfo)MyObject).Key.ToString() : "Null";
public string OutputString => !IsKey() && MyObject != null ? (string)MyObject : string.Empty;
public static event Action<string> ReadInput = delegate { };
public static event Action<ConsoleKeyInfo> ReadKey = delegate { };
private ConsoleOutput()
{
}
public ConsoleOutput(object obj)
{
MyObject = obj;
OutputType = obj is ConsoleKeyInfo ? ConsoleOutputType.Key : ConsoleOutputType.Value;
}
public bool IsKey()
{
return OutputType == ConsoleOutputType.Key;
}
public bool IsExitKey()
{
if (!IsKey())
return false;
var info = ((ConsoleKeyInfo)MyObject);
return (info.Modifiers & ConsoleModifiers.Control) != 0 && info.Key == ConsoleKey.B;
}
public string GetValue()
{
return (string)MyObject;
}
// returns null if user pressed Escape, or the contents of the line if they pressed Enter.
public static ConsoleOutput ReadLineOrKey()
{
string retString = "";
int curIndex = 0;
do
{
ConsoleKeyInfo readKeyResult = Console.ReadKey(true);
// handle Enter
if (readKeyResult.Key == ConsoleKey.Enter)
{
ReadInput?.Invoke(retString);
Console.WriteLine();
return new ConsoleOutput(retString);
}
// handle backspace
if (readKeyResult.Key == ConsoleKey.Backspace)
{
if (curIndex > 0)
{
retString = retString.Remove(retString.Length - 1);
Console.Write(readKeyResult.KeyChar);
Console.Write(' ');
Console.Write(readKeyResult.KeyChar);
--curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.Delete)
{
if (retString.Length - curIndex > 0)
{
// Store current position
int curLeftPos = Console.CursorLeft;
// Redraw string
for (int i = curIndex + 1; i < retString.Length; ++i)
Console.Write(retString[i]);
// Remove last repeated char
Console.Write(' ');
// Restore position
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Remove string
retString = retString.Remove(curIndex, 1);
}
}
else if (readKeyResult.Key == ConsoleKey.RightArrow)
{
if (curIndex < retString.Length)
{
++Console.CursorLeft;
++curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.LeftArrow)
{
if (curIndex > 0)
{
--Console.CursorLeft;
--curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.Insert)
{
IsInserting = !IsInserting;
}
#if DEBUG
else if (readKeyResult.Key == ConsoleKey.UpArrow)
{
if (Console.CursorTop > 0)
--Console.CursorTop;
}
else if (readKeyResult.Key == ConsoleKey.DownArrow)
{
if (Console.CursorTop < Console.BufferHeight - 1)
++Console.CursorTop;
}
#endif
else
// handle all other keypresses
{
if (IsInserting || curIndex == retString.Length)
{
retString += readKeyResult.KeyChar;
Console.Write(readKeyResult.KeyChar);
++curIndex;
}
else
{
// Store char
char c = readKeyResult.KeyChar;
// Write char at position
Console.Write(c);
// Store cursor position
int curLeftPos = Console.CursorLeft;
// Clear console from curIndex to end
for (int i = curIndex; i < retString.Length; ++i)
Console.Write(' ');
// Go back
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Write the chars from curIndex to end (with the new appended char)
for (int i = curIndex; i < retString.Length; ++i)
Console.Write(retString[i]);
// Restore again
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Store in the string
retString = retString.Insert(curIndex, new string(c, 1));
// Sum one to the cur index (we appended one char)
++curIndex;
}
}
if (char.IsControl(readKeyResult.KeyChar) &&
readKeyResult.Key != ConsoleKey.Enter &&
readKeyResult.Key != ConsoleKey.Backspace &&
readKeyResult.Key != ConsoleKey.Tab &&
readKeyResult.Key != ConsoleKey.Delete &&
readKeyResult.Key != ConsoleKey.RightArrow &&
readKeyResult.Key != ConsoleKey.LeftArrow &&
readKeyResult.Key != ConsoleKey.Insert)
{
#if DEBUG
if (readKeyResult.Key == ConsoleKey.UpArrow || readKeyResult.Key == ConsoleKey.DownArrow)
continue;
#endif
ReadKey?.Invoke(readKeyResult);
Console.WriteLine();
return new ConsoleOutput(readKeyResult);
}
}
while (true);
}
}
As you can see, I implemented Insert, Arrow control, Delete, etc etc... (Insert was an important thing because if you write any text with this code you will see behavior as Insert key provides).
And example of use:
internal class Program
{
private static void Main(string[] args)
{
Console.Write("Write test string: ");
var test = ConsoleOutput.ReadLineOrKey();
if (test.IsKey())
Console.WriteLine(test.KeyName);
else
Console.WriteLine($"Output string: {test.OutputString}");
Console.Read();
}
}
You can keep updated on this link (is a link to my lib I'm currently working at).