Why cant I use the {0} when writing in MsgBox? - c#

If I want to show something in a console I usually write it like this: Console.Write("Hello{0}", AnyNumber);.
But if I use Interaction.MsgBox and write Interaction.MsgBox("Hello {0}, AnyNumber"); I am getting an error, why is that?
I like to use placeholders like {0} a lot more than + (string concatenation) when writing, I think it is easier to write it like that...
So my question is: Why can't I use placeholders in MsgBox and is there any other way to use them?
Here is a example how I would like to use it in MsgBox:
static void multiplikation(int tal1, int tal2)
{
if (tal1*tal2 == 100)
{
Interaction.MsgBox("Hello!\nResult:\n" + (tal1 * tal2));
}
else
{
Interaction.MsgBox("Resultat:\n" + (tal1 * tal2));
}
}
static void addition(int tal1, int tal2)
{
if (tal1 + tal2 == 100)
{
Console.Write("Hello!\nResult:{0}\n", (tal1 + tal2));
}
else
{
Console.Write("Resultat:{0}\n", (tal1 + tal2));
}
}

Interaction.MsgBox receives a string as its first parameter. If you want to format a string, you have to do that yourself, and pass the formatted string to Interaction.MsgBox:
Interaction.MsgBox(string.Format("Hello!\nResult:{0}\n", (tal1 + tal2)));
On the other hand, Console.Write was written with overloads that allow the formatting to be performed inside Console.Write.

This is due to the fact that unlike Console.Write, Interaction.Msgbox does not know anything about string.Format: It just displays the string "as is".
Since C# version 6, there is a comfortable way:
Interaction.MsgBox($"Result:\n {tal1 * tal2}");
The '$' is telling the compiler to treat {tal1 * tal2} as an expression and insert the result into the string. More details about this so-called string interpolation:
With string interpolation, expressions within curly braces {} can
also be evaluated. The result will be inserted at the corresponding
location within the string. For example, to calculate the maximum of
foo and bar and insert it, use Math.Max within the curly
braces:Console.WriteLine($"And the greater one is: {
Math.Max(foo, bar) }");
Output:
And the greater one is: 42
Note: Any leading or trailing whitespace (including space, tab and CRLF/newline) between the curly brace and the expression is completely ignored and not included in the output
For all C# versions (especially the ones before C#6) you can do the following:
Instead of having to invoke string.Format every time, you can create a function like
public Microsoft.VisualBasic.MsgBoxResult MsgBox(string msg, params object[] p)
{
var fmt = string.Format(msg, p);
var msgStyle = Microsoft.VisualBasic.MsgBoxStyle.OkOnly;
return Interaction.MsgBox(fmt, msgStyle, "Information");
}
This can be used as follows:
var tal1=1; var tal2=2;
MsgBox("Hello!\nResult:{0}\n", (tal1 + tal2));
Note: This solution supports not only {0}, but also {1}, {2}, ... because I have used a params array. Hence, the following is working too:
MsgBox("Calculation: {0} + {1} = {2}", tal1, tal2, (tal1+tal2));
which outputs Calculation: 1 + 2 = 3.
You can read here (at Microsoft) more about the Interaction.MsgBox method, how to change the style of the messagebox (e.g. how to display Yes/No/Cancel buttons and about the MsgBoxResult values being returned) so you can tailor it to your needs.
This is the complete program, you can run it in LinqPad or Visual Studio:
static void Main(string[] args)
{
var tal1=1; var tal2=2;
MsgBox("Hello!\nResult:{0}\n", (tal1 + tal2));
MsgBox("Calculation: {0} + {1} = {2}", tal1, tal2, (tal1+tal2));
}
public static MsgBoxResult MsgBox(string msg, params object[] p)
{
var fmt=string.Format(msg, p);
var msgStyle= Microsoft.VisualBasic.MsgBoxStyle.OkOnly;
return Interaction.MsgBox(fmt, msgStyle, "Information");
}
Don't forget to add a reference to Microsoft.VisualBasic.dll, using the namespace Microsoft.VisualBasic in order to run this example.
If you're using LinqPad, right-click, choose "Query properties..." then add the dll in "Additional references" and the namespace in "Additional Namespace imports".

Console.WriteLine has an overloads which supports a string.Format type implementation.
http://msdn.microsoft.com/en-us/library/586y06yf(v=vs.110).aspx
As pointed out in the comments, you will need to use string.Format() to replicate that behavior for Interaction.MsgBox.

you can use the string format method http://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx

Related

How do I tell the difference between +'s in and out of strings?

I am making a simple compiler, and am working on string parsing. At the moment, my code is:
while (stringToParse.Contains(" + ") || stringToParse.Contains("+ ") || stringToParse.Contains(" +")) {
stringToParse = stringToParse.Replace(" +", "+").Replace("+ ", "+").Replace(" + ", "+");
}
string[] splitString = stringToParse.Split("+");
But something like:
"\"hello \" + \"world \" + \" + \" + \"hello\""
Would return:
["\"hello "\", "\"world \"", "\"", "\"", ]
(without backslashes)
But something like:
""hello " + "world " + " + " + "hello""
Would return:
[""hello "", ""world "", """, """, ]
So how can I specify if a " + " is in a string or as a separator? is there maybe a way to detect for something like the following?
...(any number of non " or + characters)...+...(any number of " or + characters)
My expected output would be:
[""hello "", ""world "", ""+""]
Explicit State Machine
To do this, Without using any dedicated library, I suggest to build a state machine.
You will iterate over the characters of the string, and depending on which character you encounter you update the state of the machine. Optimizations are possible, however, let us begin with conventional clarity.
var characters = input.ToCharArray();
var results = new List<string>();
var current = string.Empty;
// 0 = not inside quotes, we expect +
// 1 = not inside quotes, we expect "
// 2 = inside quotes
var state = 1;
foreach (var character in characters)
{
switch (state)
{
case 0:
// We are not inside quotes, we expect +
if (character == '+')
{
state = 1;
continue;
}
if (char.IsWhiteSpace(character))
{
continue;
}
// error?
break;
case 1:
// We are not inside quotes, we expect "
if (character == '\"')
{
state = 2;
continue;
}
if (char.IsWhiteSpace(character))
{
continue;
}
// error?
break;
case 2:
// We are inside quotes, we expect "
if (character == '\"')
{
state = 0;
results.Add(current);
current = string.Empty;
continue;
}
current += character;
break;
default:
// error?
break;
}
}
if (state != 0)
{
// error
}
// You can use results.ToArray();
Possible optimizations:
We can use a StringBuilder instead of concatenations.
Also, we can use IndexOf to find the next relevant character.
We can check if a string (a chunk of characters) is empty or white space (perhaps using IsNullOrWhiteSpace).
We can use AsSpan so we can work with ReadOnlySpan instead.
You can also see how you can add support for your own escape sequences, or any other stuff.
Implicit State Machine (with helper class)
I want to point out that this is not the only way to organize this code. I would, if I were you, create a pseudo iterator class that had a method two methods:
A method that returns the next character... or better yet, that returns true if the next character matches a parameter (and advances), or false (and does not advance).
A method that returns all the characters until the next instance of a particular character (and advances to there).
The main advantage of such approach is that I would no longer have to step character by character, thus, I would not need to have a state variable. Instead I could allow the code structure to resemble the shape of my gramar.
Wait, I have wrote such class: StringProcessor. It is part of the Theraot.Core nuget, it is used to parse strings to BigInteger.
var processor = new Theraot.Core.StringProcessor(input);
var results = new List<string>();
while (!processor.EndOfString)
{
// SkipWhile skips all the characters that match
processor.SkipWhile(char.IsWhiteSpace);
// Read returns true (and advances after) if what is next matches the paramter
if (processor.Read('"'))
{
// ReadUntil advances after and returns everything found before the parameter
// Note: it does not advance after the parameter.
results.Add(processor.ReadUntil('"'));
processor.Read('"');
}
processor.SkipWhile(char.IsWhiteSpace);
if (!processor.Read('+'))
{
// error?
}
}
Please notice that a class such as the StringProcessor used above cuts a lot of fluff, which makes it viable for simple languages.
Custom Tokenizer
Of course, for something more complex you might want to look for a tokenizer.
To give you an example, consider that this is the "grammar" we have:
Document: Many
{
Whitespace
String:
{
QuoteSymbol
NonQuoteSymbol
QuoteSymbol
}
Whitespace
PlusSymbol
}
No, this not any of the usual metalanguages. However, written this way it is easier to see how the code we had above resembles the language.
Would it not be nice to write as follows?
var QuoteSymbol = Pattern.Literal("QuoteSymbol", '"');
var NonQuoteSymbol = Pattern.Custom("NonQuoteSymbol", s => s.ReadUntil('"'));
var String = Pattern.Conjunction("String", QuoteSymbol, NonQuoteSymbol, QuoteSymbol);
var WhiteSpace = Pattern.Custom("WhiteSpace", s => s.ReadWhile(char.IsWhiteSpace));
var PlusSymbol = Pattern.Literal("PlusSymbol", '+');
var Document = Pattern.Repetition(
Pattern.Conjunction(WhiteSpace, String, WhiteSpace, PlusSymbol)
);
var results = from TerminalSymbol symbol
in Document.Parse(input)
where symbol.Pattern == String
select symbol.ToString();
Writing code like that would make it easier to modify the language. Well, we are still writing code, however you could imagine parsing a file that has the grammar of the language you want to parse... Fancy!
As you might expect, it requires extra work to build the necesary code to make it work. Or, you know, get some code that already works (the linked code is built around on StringProcessor).
Language Toolkits
The code presented earlier is not suitable to be used for a prettyprinter and is not capable of recovering from a syntax error. It can be modified to do such things. Neither will it integrate with code editors at any level.
If you want a fully fledged solution. I have two suggestions:
Irony
Nitra
These are the kind of things you would use if you wanted to create a programming language ontop.
And of course, I should link you to "Compilers: Principles, Techniques, and Tools" usually just known as "The Dragon Book".

C# split string.format string with regex

I want to be able to split a formatting string on the variables' position indicators. It will chop out the curly braces and the position indicating numbers in between them.
So, the string:
string format = "{0} field needs to be set to '{1}' when {2}, Fixit. NOW!";
Should resolve to 3 strings.
" field needs to be set to '"
"' when "
", Fixit. NOW!"
We use string like the above 'format' to build error messages. My goal is to add generic unit tests that can take in the format and verify that an error message was generated that matches the expected format. Since both the error generation code and the unit test reference the same formatting, the unit tests won't need to be updated when minor changes are made to the message.
In the above example, i'll be able to test for the expected results, via a call to a new method called SplitFormatString.
string fieldName = "UserName";
string expectedValue = "Bob";
string condition = "excellence is a must";
string errorMessage = TestFieldValueErrorCase( );
AssertStringContainsAllThese(errorMessage, SplitFormatString(format), fieldName, expectedValue,condition);
With validation
public static void AssertStringContainsAllThese(string msg, string[] formatChunks, params string[] thingsToFind)
{
foreach (var txt in formatChunks.Concat(thingsToFind))
{
Assert.IsTrue(msg.Contains(txt), "Could not find <<" + txt + ">> in msg >>> " + msg);
}
}
I'd rather use regex than an inelegant approach using substrings.
I think you will want Regex.Split
Splits an input string into an array of substrings at the positions defined by a regular expression pattern.
Regex.Split("{0} field needs to be set to '{1}' when {2}, Fixit. NOW!", #"{\d+}");
Should output:
["","field needs to be set to '","' when ",", Fixit. NOW!"]

Passing Quotation Mark Character (") as C# Console Application Argument

I have a project to demonstrate a program similar to the "echo" command in the MS-DOS Command Line. Here is the code in C#:
using System;
namespace arguments
{
class Program
{
static void Main(string[] args)
{
try
{
switch (args[0])
{
case "/?":
string location = System.Reflection.Assembly.GetEntryAssembly().Location;
string name = System.IO.Path.GetFileName(location);
Console.WriteLine("Displays messages\nSyntax: {0} [message]", name);
Environment.Exit(0);
break;
}
if (args.Length >= 0)
{
string x = "";
foreach (var item in args)
{
x += item.ToString() + " ";
}
Console.WriteLine(Convert.ToString(x)); // this should eliminate vulnerabilities.
}
}
catch
{
string location = System.Reflection.Assembly.GetEntryAssembly().Location;
string name = System.IO.Path.GetFileName(location);
Console.WriteLine("Displays messages\nSyntax: {0} [message]", name);
}
}
}
}
This does a pretty efficient job at doing what it's supposed to do. Then I got into trying to exploit it in any way I could.
In command prompt, I ran arguments.exe ", this is supposed to print out ". But that's not really what happened. I then tried the same with the echo command by running echo ", and it, like it's supposed to, printed out ". This is mind boggling because I wouldn't have even thought this would be a problem. I couldn't get it to pose a great threat, just confused me for a minute.
My question is, is there any way to pass the quotation mark (") as argument to this console application?
Here is a picture to demonstrate it a little bit better: http://prntscr.com/cm9yal
void Main(string[] args)
args array here contains the arguments which have been passed to your application. Because arguments may have spaces they can be surrounded by quotes.
For this reason you won't get the string you have placed as argument. You will also loose any number of spaces between quoted parameters.
If you need the raw command line string, use:
string cmdline = System.Environment.CommandLine;
To be able to get the single quote, you'll need to bypass the default parsing performed by the CLR when populating the args array. You can do this by examining Environment.CommandLine, which in the case you describe above will return something along the lines of:
ConsoleApplication1.exe \"
Note, the argument I passed was simply " (not the escaped variant shown).

Add to string if string non empty

Sometime I want to join two strings with a space in between. But if second string is null, I don't want the space.
Consider following code:
void AssertFoo(bool cond, string message = null) {
...
Assert.Fail("Something is foo.{0}", message != null ? " " + message : "");
...
}
Is there a more elegant way to do that?
Here is one option that I like. It's better if you already have an IEnumerable<string> with your data, but it's easy enough even if you don't. It also clearly scales well to n strings being joined, not just 1 or two.
string[] myStrings = new string[]{"Hello", "World", null};
string result = string.Join(" ", myStrings.Where(str => !string.IsNullOrEmpty(str)));
Here is another option. It's a bit shorter for this one case, but it's uglier, harder to read, and not as extensible, so I would probably avoid it personally:
//note space added before {0}
Assert.Fail("Something is foo. {0}", message ?? "\b");
In this case we add the space to the format string itself, but if message is null we instead use the backspace character to remove the space that we know is before it in the message.
For newer versions of C# you can use the following extension method:
public static string Prepend(this string value, string prepend) => prepend + value;
It can be used like this:
Assert.Fail("Something is foo.{0}", message?.Prepend(" "));
Added in 2020:
Today I use this:
public static string Surround(this object value, string prepend, string append = null) => prepend + value + append;
Try this:
string joinedString = string.IsNullOrEmpty(message2) ? message1 : message1 + " " + message2;
Assert.Fail("Something is foo.{0}", (" " + message).TrimEnd());
Sure, this will result in a few string object creations, but it's unlikely such micro-optimization issues would matter in the vast majority of programs. It might be considered an advantage of this method that it handles not just null message, but a message of all whitespace as well.
Assert.Fail("Something is foo.{0}", message?.PadLeft(message.Lenght + 1, ' '));
Since C#6 you can use string interpolation like this:
$"Something is foo. {mssg}".TrimEnd();
See it in .NET Fiddle
The most elegant way is to use the inbuilt keyword of String class.
String.IsNullOrEmpty
This way you wont have a problem.

Regular expression that returns a constant value as part of a match

I have a regular expression to match 2 different number formats: \=(?[0-9]+)\?|\+(?[0-9]+)\?
This should return 9876543 as its Value for ;1234567890123456?+1234567890123456789012345123=9876543? and ;1234567890123456?+9876543?
What I would like is to be able to return another value along with the matched 'Value'.
So, for example, if the first string was matched, I'd like it to return:
Value:
9876543
Format:
LongFormat
And if matched in the second string:
Value:
9876543
Format:
ShortFormat
Is this possible?
Another option, which is not quite the solution you wanted, but saves you using two separate regexes, is to use named groups, if your implementation supports it.
Here is some C#:
var regex = new Regex(#"\=(?<Long>[0-9]+)\?|\+(?<Short>[0-9]+)\?");
string test1 = ";1234567890123456?+1234567890123456789012345123=9876543?";
string test2 = ";1234567890123456?+9876543?";
var match = regex.Match(test1);
Console.WriteLine("Long: {0}", match.Groups["Long"]); // 9876543
Console.WriteLine("Short: {0}", match.Groups["Short"]); // blank
match = regex.Match(test2);
Console.WriteLine("Long: {0}", match.Groups["Long"]); // blank
Console.WriteLine("Short: {0}", match.Groups["Short"]); // 9876543
Basically just modify your regex to include the names, and then regex.Groups[GroupName] will either have a value or wont. You could even just use the Success property of the group to know which matched (match.Groups["Long"].Success).
UPDATE:
You can get the group name out of the match, with the following code:
static void Main(string[] args)
{
var regex = new Regex(#"\=(?<Long>[0-9]+)\?|\+(?<Short>[0-9]+)\?");
string test1 = ";1234567890123456?+1234567890123456789012345123=9876543?";
string test2 = ";1234567890123456?+9876543?";
ShowGroupMatches(regex, test1);
ShowGroupMatches(regex, test2);
Console.ReadLine();
}
private static void ShowGroupMatches(Regex regex, string testCase)
{
int i = 0;
foreach (Group grp in regex.Match(testCase).Groups)
{
if (grp.Success && i != 0)
{
Console.WriteLine(regex.GroupNameFromNumber(i) + " : " + grp.Value);
}
i++;
}
}
I'm ignoring the 0th group, because that is always the entire match in .NET
No, you can't match text that isn't there. The match can only return a substring of the target.
You essentially want to match against two patterns and take different actions in each case. See if you can separate them in your code:
if match(\=(?[0-9]+)\?) then
return 'Value: ' + match + 'Format: LongFormat'
else if match(\+(?[0-9]+)\?) then
return 'Value: ' + match + 'Format: ShortFormat'
(Excuse the dodgy pseudocode, but you get the idea.)
You can't match text that isn't there - but, depending on what language you're using, you can process what you match, and conditionally add text based on what is there.
With some implementations of regex, you can specify a "callback function" which allows you to run logic against each result.
Here's a pseudo-code example:
Input.replaceAll( /[+=][0-9]+(?=\?)/ , formatValue );
formatValue : function(match,groups)
{
switch( left(match,1) )
{
case '+' : Format = 'Short'; break;
case '=' : Format = 'Long'; break;
default : Format = 'Unknown'; break;
}
Value : match.replace('[+=]');
return 'Value: '+Value+' Format: ' + Format;
}
What that will do, in a language that supports regex callbacks, is execute the formatValue function every time it finds a match, and use the result of the function as the replacement text.
You haven't specified which implementation you're using, so this may or not be possible for you, but it is definitely worth checking out.

Categories