This question already has answers here:
Identify if a string is a number
(26 answers)
Closed 6 years ago.
I am writing a small console application in C# and it requires two numeric parameters/arguments to be passed to it. I am checking for no params, more than 2 params, if two params. I am also converting those params if 2 returned to numeric for calculations I'm doing. One thing I'm not doing is verifying what's being passed is a number so assume program will crap out if I pass a letter. Is there a look routing I can just run to verify the params passed are in deed able to be numbers? I can handle the kick out from there but curious how you check if an argument CAN be a number or not.
Thanks.
JR
I would simplify this task as follows:
Create custom Convert method:
static double Convert(string s)
{
double result;
if (!double.TryParse(s, out result))
result = double.NaN;
return result;
}
Use it in Main:
static void Main(string[] args)
{
var doubles = args.Select(a => Convert(a)).ToArray();
var valid = doubles.All(a => !double.IsNaN(a));
}
Here's one way;
static void Main(string[] args)
{
foreach (var arg in args)
{
int asInt;
// Returns "true" if arg can be parsed as an int and false otherwise;
// If you want to allow doubles you can also try double.TryParse in a similar manner
if (int.TryParse(arg, out asInt))
{
// Handle
}
// Failed to parse argument as an int
else
{
throw new ArgumentException("arg not an int");
}
}
}
You could also use a Regex:
static void Main(string[] args)
{
// Make sure this can is either an int or a double
var regex = new Regex(#"^-?(([0-9]+)|([0-9]+\.[0-9]+(e[0-9]+)?))$");
foreach (var arg in args)
{
if (!regex.IsMatch(arg))
throw new ArgumentException("arg " + arg + " is not an int or double");
}
}
Note a few important features of this regex:
- The "#" literal symbol in front of the regex string
- The ^ and $ to mark the beginning and end of lines - i.e. the string must contain only a number or only a double
- This bans empty strings as well
Edit: I edited the Regex to optionally allow for something like "1.0e100" or "-123", which, as pointed out in the comments, are also perfectly valid ints and doubles. Also, as pointed out in the comments, it's probably better to use int.TryParse or double.TryParse rather than reinventing the wheel.
There's also Char.IsDigit which always feels cleaner to me than checking for a failed parsing attempt. If you care about micro-optimizing, char.IsDigit is probably faster than parsing an invalid string too.
Or, if negative numbers or non-whole numbers are your thing, you can use char.IsNumber
See: https://msdn.microsoft.com/en-us/library/7f0ddtxh(v=vs.110).aspx
public static void Main(string[] args)
{
for(int i = 0; i < args.Length; i++)
{
if(args[i].All((c) => char.IsDigit(c))
{
//It's a number.
}
}
}
Related
I know it's possible to check whether the value of a text box or variable is numeric using try/catch statements, but IsNumeric is so much simpler. One of my current projects requires recovering values from text boxes. Unfortunately, it is written in C#.
I understand that there's a way to enable the Visual Basic IsNumeric function in Visual C# by adding a reference to Visual Basic, though I don't know the syntax for it. What I need is a clear and concise walkthrough for enabling the IsNumeric function in C#. I don't plan on using any other functions indigenous to Visual Basic.
public bool IsNumeric(string value)
{
return value.All(char.IsNumber);
}
To totally steal from Bill answer you can make an extension method and use some syntactic sugar to help you out.
Create a class file, StringExtensions.cs
Content:
public static class StringExt
{
public static bool IsNumeric(this string text)
{
double test;
return double.TryParse(text, out test);
}
}
EDIT: This is for updated C# 7 syntax. Declaring out parameter in-line.
public static class StringExt
{
public static bool IsNumeric(this string text) => double.TryParse(text, out _);
}
Call method like such:
var text = "I am not a number";
text.IsNumeric() //<--- returns false
You could make a helper method. Something like:
public bool IsNumeric(string input) {
int test;
return int.TryParse(input, out test);
}
It is worth mentioning that one can check the characters in the string against Unicode categories - numbers, uppercase, lowercase, currencies and more. Here are two examples checking for numbers in a string using Linq:
var containsNumbers = s.Any(Char.IsNumber);
var isNumber = s.All(Char.IsNumber);
For clarity, the syntax above is a shorter version of:
var containsNumbers = s.Any(c=>Char.IsNumber(c));
var isNumber = s.All(c=>Char.IsNumber(c));
Link to unicode categories on MSDN:
UnicodeCategory Enumeration
Using C# 7 (.NET Framework 4.6.2) you can write an IsNumeric function as a one-liner:
public bool IsNumeric(string val) => int.TryParse(val, out int result);
Note that the function above will only work for integers (Int32). But you can implement corresponding functions for other numeric data types, like long, double, etc.
http://msdn.microsoft.com/en-us/library/wkze6zky.aspx
menu:
Project-->Add Reference
click: assemblies, framework
Put a checkmark on Microsoft.VisualBasic.
Hit OK.
That link is for Visual Studio 2013, you can use the "Other versions" dropdown for different versions of visual studio.
In all cases you need to add a reference to the .NET assembly "Microsoft.VisualBasic".
At the top of your c# file you neeed:
using Microsoft.VisualBasic;
Then you can look at writing the code.
The code would be something like:
private void btnOK_Click(object sender, EventArgs e)
{
if ( Information.IsNumeric(startingbudget) )
{
MessageBox.Show("This is a number.");
}
}
Try following code snippet.
double myVal = 0;
String myVar = "Not Numeric Type";
if (Double.TryParse(myVar , out myNum)) {
// it is a number
} else {
// it is not a number
}
I usually handle things like this with an extension method. Here is one way implemented in a console app:
namespace ConsoleApplication10
{
class Program
{
static void Main(string[] args)
{
CheckIfNumeric("A");
CheckIfNumeric("22");
CheckIfNumeric("Potato");
CheckIfNumeric("Q");
CheckIfNumeric("A&^*^");
Console.ReadLine();
}
private static void CheckIfNumeric(string input)
{
if (input.IsNumeric())
{
Console.WriteLine(input + " is numeric.");
}
else
{
Console.WriteLine(input + " is NOT numeric.");
}
}
}
public static class StringExtensions
{
public static bool IsNumeric(this string input)
{
return Regex.IsMatch(input, #"^\d+$");
}
}
}
Output:
A is NOT numeric.
22 is numeric.
Potato is NOT numeric.
Q is NOT numeric.
A&^*^ is NOT numeric.
Note, here are a few other ways to check for numbers using RegEx.
Tested with Net6 and universal with object because needed in my app:
public static bool IsNumeric(this object text) => double.TryParse(Convert.ToString(text), out _);
Works with null and string.empty and also tested "".
Is numeric can be achieved via many ways, but i use my way
public bool IsNumeric(string value)
{
bool isNumeric = true;
char[] digits = "0123456789".ToCharArray();
char[] letters = value.ToCharArray();
for (int k = 0; k < letters.Length; k++)
{
for (int i = 0; i < digits.Length; i++)
{
if (letters[k] != digits[i])
{
isNumeric = false;
break;
}
}
}
return isNumeric;
}
I am not very experienced with C# and have got a task to develop a simple program that can take two parameters; one string and one integer. The string is to be returned, and the integer is supposed to define the max number of each specific character that is returned in the string, i.e.:
input: "aaabbbbc", "2" output: aabbc
input: "acc", "1" output: ac
I've tried looking at different collections like IEnumerator to help make the code easier to write, but as I'm not very experienced I can't sort out how to utilize them.
This is the code that I've written so far:
public static string TwoParameters()
{
Console.Write("Write some characters (i.e. 'cccaabbb'): ");
string myString = Console.ReadLine();
return myString;
Console.Write("Write a number - ");
int max = Convert.ToInt32(Console.Read());
}
public static void Counter(string myString, int max)
{
string myString = TwoParameters(myString);
foreach (char a in myString)
{
var occurences = myString.Count(x => x == 'a');
if (occurences > max)
max = occurences;
}
}
Errors I get when running:
CS0136: Local or parameter 'myString' cannot be declared in scope because of enclosing local scope.
CS1501: No overload for method 'TwoParameters' takes 1 arg.
CS1061: 'string' does not contain a definition for count.
CS0165: Use of unassigned local variable 'myString'.
CS7036: There is no argument given that corresponds to the required formal parameter 'myString' of 'Program.Counter(string, int)'
Any kind of pointers to what I'm doing wrong, suggestions to how I can improve my code and/or finish it up for the program to make the output will be hugely appreciated.
A string can be treated as an IEnumerable<char>. You can use LINQ to first group the characters then take only 2 from each group, eg :
var input="aaabbbbc";
var max=2;
var chars=input.GroupBy(c=>c)
.SelectMany(g=>g.Take(2))
.ToArray();
var result=new String(chars);
This produces
aabbc
This query groups the characters together with GroupBy and then takes only max from each group with Take. SelectMany flattens all the IEnumerable<char> returned from Take into a single IEnumerable<char> that can be used to create a string
This function would also respect the order within the string, so aabcabc, 2 would result into aabcbc:
static string ReturnMaxOccurences(string source, int count)
{
return source.Aggregate(new Accumulator(), (acc, c) =>
{
acc.Histogram.TryGetValue(c, out int charCount);
if (charCount < count)
acc.Result.Append(c);
acc.Histogram[c] = ++charCount;
return acc;
}, acc => acc.Result.ToString());
}
But you need also this little helper class:
public class Accumulator
{
public Dictionary<char, int> Histogram { get; } = new Dictionary<char, int>();
public StringBuilder Result { get; } = new StringBuilder();
}
This method iterates the whole string and save within a histogram the occurences of each character. If the value is lower than the desired max value it will be added to the result string, otherwise it simply steps over the character and continues with the next.
Pointers to what you are doing wrong:
you reset your given max to the counts in your string
you only handle "a"
your TwoParameters function has unreachable code
you try to declare a variable name again already providing it to the function as parameter
you do not build a string to output
Using Linq is probably somewhat overkill for your level of knowledge. This is a simpler Version of Oliver's answer - respecting order of letters as well:
public static void Main()
{
var input = "abbaaabcbcccd";
var max = 2;
// stores count of characters that we added already
var occurences = new Dictionary<char,int>();
var sb = new StringBuilder();
foreach (var c in input)
{
// add with a count of 0 if not yet encountered
if (!occurences.ContainsKey(c))
{
// if you want only "consecutive" letters not repeated over max:
// uncomment the following line that resets your dict - you might
// want to use a single integer and character instead in that case
// occurences.Clear(); // better use a single int-counter instead
occurences[c] = 0;
}
// add character if less then max occurences
if (occurences[c] < max)
{
sb.Append(c);
occurences[c]+=1;
}
}
Console.WriteLine(sb.ToString());
}
Output:
abbaccd
I have a (hopefully) simple C# question.
I am parsing arguments in a program where a file will be read from command line, I've allowed for both short and long arguments as input (so for my scenario /f and file are both valid)
The value after either of the above arguments should be the file name to be read.
What I want to do is find this file name in the array based off whichever argument is chosen and copy it in to a string while not leaving any loopholes.
I have functioning code, but I'm not really sure it's "efficient" (and secure).
Code (comments and writes removed):
if ( args.Contains("/f") || args.Contains("file"))
{
int pos = Array.IndexOf(args, "/f");
if (pos == -1)
pos = Array.IndexOf(args, "file");
if (pos > -1)
pos++;
inputFile = (args[pos]);
if (File.Exists(inputFile) == false)
{
Environment.Exit(0);
}
}
Is there a more efficient way to do this, perhaps using some nifty logic in the initial if statement to check which parameter is valid and then do a single check on that parameter?
Using 4 ifs and 2 Array.IndexOf's seems horrible just to support 2 different ways to allow someone to say they want to input a file...
Thanks! And I'm sorry if this seems trivial or is not what SO is meant for. I just don't have any real way to get feedback on my coding practises unfortunately.
Your solution won't scale well. Imagine you have two different arguments with a short and long form. How many conditionals and index checks would that be?
You'd be better off using an existing tool (e.g. Command Line Parser Library) for argument parsing.
One problem I see with the code you provided is that, it will fail if the /f or file is the last argument.
If you don't want to write or use complete argument parsing code, the following code will work slightly better.
var fileArguments = new string[] { "/f", "file" };
int fileArgIndex = Array.FindIndex(args,
arg => fileArguments.Contains(arg.ToLowerInvariant()));
if (fileArgIndex != -1 && fileArgIndex < args.Length - 1)
{
inputFile = args[fileArgIndex + 1];
if (!File.Exists(inputFile))
{
Environment.Exit(0);
}
}
You could write a simple argument parser for your specific need and still have support for "new" scenarios. For example, in your entry method have
// The main entry point for the application.
[STAThread]
static void Main(string[] args)
{
// Parse input args
var parser = new InputArgumentsParser();
parser.Parse(args);
....
}
Where your InputArgumentsParser could be something similar to
public class InputArgumentsParser
{
private const char ArgSeparator = ':';
private Dictionary<string[],Action<string>> ArgAction =
new Dictionary<string[],Action<string>>();
public InputArgumentsParser()
{
// Supported actions to take, based on args
ArgAction.Add(new[] { "/f", "/file" }, (param) =>
Console.WriteLine(#"Received file argument '{0}'", param));
}
/// Parse collection, expected format is "<key>:<value>"
public void Parse(ICollection<string> args)
{
if (args == null || !args.Any())
return;
// Iterate over arguments, extract key/value pairs
foreach (string arg in args)
{
string[] parts = arg.Split(ArgSeparator);
if (parts.Length != 2)
continue;
// Find related action and execute if found
var action = ArgAction.Keys.Where(key =>
key.Contains(parts[0].ToLowerInvariant()))
.Select(key => ArgAction[key]).SingleOrDefault();
if (action != null)
action.Invoke(parts[1]);
else
Console.WriteLine(#"No action for argument '{0}'", arg);
}
}
}
In this case /f:myfile.txt or /file:myfile.txt would spit out to console
Received file argument 'myfile.txt'
Currently I'm working on an web-API dependent application. I have made a class with a lot of API strings like: /api/lol/static-data/{region}/v1/champion/{id}. Also I made a method:
public static String request(String type, Object[] param)
{
return "";
}
This is going to do the requesting stuff. Because it's very different with each request type how many parameters are being used, I'm using an array for this. Now the question is, is it posssible to String.Format using an array for the paramaters while the keys in the strings are not numbers? Or does anyone know how to do this in a different way?
No, string.Format only supports index-based parameter specifications.
This:
"/api/lol/static-data/{region}/v1/champion/{id}"
^^^^^^^^ ^^^^
will have to be handled using a different method, like string.Replace or a Regex.
You will need to:
Decide on the appropriate method for doing the replacements
Decide how an array with index-based values should map to the parameters, are they positional? ie. {region} is the first element of the array, {id} is the second, etc.?
Here is a simple LINQPad program that demonstrates how I would do it (though I would add a bit more error handling, maybe caching of the reflection info if this is executed a lot, some unit-tests, etc.):
void Main()
{
string input = "/api/lol/static-data/{region}/v1/champion/{id}";
string output = ReplaceArguments(input, new
{
region = "Europe",
id = 42
});
output.Dump();
}
public static string ReplaceArguments(string input, object arguments)
{
if (arguments == null || input == null)
return input;
var argumentsType = arguments.GetType();
var re = new Regex(#"\{(?<name>[^}]+)\}");
return re.Replace(input, match =>
{
var pi = argumentsType.GetProperty(match.Groups["name"].Value);
if (pi == null)
return match.Value;
return (pi.GetValue(arguments) ?? string.Empty).ToString();
});
}
What is the best way to take a string which can be empty or contain "1.2" for example, and convert it to an integer? int.TryParse fails, of course, and I don't want to use float.TryParse and then convert to int.
Solution 1: Convert.ToDouble (culture-dependent)
You may using Convert.ToDouble. But, beware! The below solution will work only when the number separator in the current culture's setting is a period character.
var a = (int)Convert.ToDouble("1.2");
Solution 2: Convert.ToDouble (culture-independent)
It's preferable to use IFormatProvider and convert the number in an independent way from the current culture settings:
var a = (int)Convert.ToDouble("1.2", CultureInfo.InvariantCulture.NumberFormat);
Solution 3: Parse & Split
Another way to accomplish this task is to use Split on parsed string:
var a = int.Parse("1.2".Split('.')[0]);
Or:
var a = int.Parse("1.2".Split('.').First());
Notes
If you want to handle empty and null strings, write a method and add string.IsNullOrEmpty condition.
To get decimal separator for the current culture setting, you can use NumberFormatInfo.NumberDecimalSeparator property.
You should also keep eye on rounding to avoid traps.
Select casting, Parse, TryParse or Convert class wisely. Read more at:
How to: Convert a string to an int (C# Programming Guide)
How to: Determine Whether a String Represents a Numeric Value (C# Programming Guide)
I don't know what's wrong with parsing to a float and converting to an int. I doubt that any other way would be more efficient but here's an attempt:
//allows empty strings and floating point values
int ParseInt(string s, bool alwaysRoundDown = false)
{
//converts null/empty strings to zero
if (string.IsNullOrEmpty(s)) return 0;
if (!s.Contains(".")) return int.Parse(s);
string parts = s.Split(".");
int i = int.Parse(parts[0]);
if (alwaysRoundDown || parts.Length==1) return i;
int digitAfterPoint = int.Parse(parts[1][0]);
return (digitAfterPoint < 5) ? i : i+1;
}
In order to globalize the code you would need to replace "." with System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator.
int a = (int)Math.Round(float.Parse("0.9"));
You need to round it first unless you want 0.9f being converted to 0 instead of 1.
Maybe you can try to delete everything after floating point using string functions and then convert to int. But seriously I don't think it's better than converting to float and then to int.
I think another way of doing it would be splitting the string into pieces taking the decimal (.) as the delimiter and then parsing for the integer. Of course, I am yet to ask you if the string might contain values like "37.56 miles in 32.65 seconds" type values.
Considering there will be only one value (string or number) in the string, I can think of something in the following line:
public int64 GetInt64(string input)
{
if (string.IsNullOrEmpty(input)) return 0;
// Split string on decimal (.)
// ... This will separate all the digits.
//
string[] words = input.Split('.');
return int.Parse(words[0]);
}
You can use the Visual Basic runtime Library to accomplish this from c#.
You need to add a reference to the assembly Microsoft.VisualBasic.dll to your solution.
Then the following code will do your conversion:
using VB = Microsoft.VisualBasic.CompilerServices;
class Program
{
static void Main(string[] args)
{
int i = VB.Conversions.ToInteger("1.2");
}
}
I had this same problem and ended up using a hybrid of Mark's and Dariusz':
if (num == "")
{
num = "0.00";
}
var num1 = (float)Convert.ToDouble(num);