Reputation: 33
So I managed to kind of solve that problem, but I'm pretty sure there is a better way to do it. Plus, the second while statement works, but if I enter 6 digits, the first while won't apply to the input.
inputNumber = Console.ReadLine();
// checks if the value entered by user consists of 5 numbers
while (inputNumber.Length != 5)
{
// if the length of the value doesn't equal to 5, the program
// prompts the user to enter a valid number until there are 5 digits
Console.WriteLine("Please make sure you are entering a 5-Digit number.");
Console.WriteLine("Enter a 5-digit Number:");
inputNumber = Console.ReadLine();
}
// checks if there are any letters in the input
while (inputNumber.Contains("a") ||
inputNumber.Contains("b") ||
inputNumber.Contains("c") ||
inputNumber.Contains("d") ||
inputNumber.Contains("e")) // and so on
{
Console.WriteLine("You can only use integer values! Please try again:");
inputNumber = Console.ReadLine();
}
Upvotes: 2
Views: 73
Reputation: 81513
You can use regex, which is a very common and powerful string input validation tool:
Update : I am using [0-9]
instead of \d
which is a fair comparison with the old school way to match ascii digits only
!Regex.IsMatch(inputNumber, @"[0-9]{" + N + "}$", RegexOptions.ECMAScript)
or
Update : I have added ascii comparison and using c#9 patterns
inputNumber.Length != 5 || !inputNumber.All(x => x is >= '0' and <= '9')
or super old-school and efficient:
Update : I have added c#9 patterns
public bool Validate(str input)
{
if (_str.Length != 5)
return false;
foreach (var c in _str)
if (c is < '0' or > '9')
return false;
return true;
}
Note: In all these examples you only need one loop as they perform both length and digit evaluation.
Example
while (inputNumber.Length != 5 || !inputNumber.Any(char.IsDigit))
{
...
Console.WriteLine("Please make sure you are entering a 5-Digit number.");
Console.WriteLine("Enter a 5-digit Number:");
inputNumber = Console.ReadLine();
}
However, for no good reason whatsoever, let's benchmark them with varying length input
Setup
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1316 (1909/November2018Update/19H2)
AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores
.NET Core SDK=5.0.102
[Host] : .NET Core 5.0.2 (CoreCLR 5.0.220.61120, CoreFX 5.0.220.61120), X64 RyuJIT
.NET Core 5.0 : .NET Core 5.0.2 (CoreCLR 5.0.220.61120, CoreFX 5.0.220.61120), X64 RyuJIT
Job=.NET Core 5.0 Runtime=.NET Core 5.0
Results
Method | N | Mean | Error | StdDev |
---|---|---|---|---|
CompiledRegex | 5 | 45.814 ns | 0.5341 ns | 0.4996 ns |
Linq | 5 | 39.255 ns | 0.8159 ns | 0.9396 ns |
OldSchool | 5 | 2.747 ns | 0.0068 ns | 0.0053 ns |
CompiledRegex | 1000 | 549.921 ns | 3.3407 ns | 3.1249 ns |
Linq | 1000 | 5,727.363 ns | 107.6484 ns | 150.9081 ns |
OldSchool | 1000 | 382.726 ns | 0.1857 ns | 0.1450 ns |
CompiledRegex | 10000 | 5,017.362 ns | 11.0595 ns | 9.8039 ns |
Linq | 10000 | 57,502.325 ns | 218.1421 ns | 204.0503 ns |
OldSchool | 10000 | 3,764.997 ns | 3.4676 ns | 3.0739 ns |
Code
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public class Test
{
[Params(5, 1000, 10000)]
public int N;
private string _str;
private Regex _compiledRegex;
[GlobalSetup]
public void Setup()
{
var r = new Random(42);
_str = string.Concat(Enumerable.Range(0, N).Select(x => (char)r.Next('0', '9')));
_compiledRegex = new Regex(@"[0-9]{" + N + "}$", RegexOptions.Compiled|RegexOptions.ECMAScript);
}
[Benchmark]
public bool CompiledRegex() => !_compiledRegex.IsMatch(_str);
[Benchmark]
public bool Linq() => _str.Length != N || !_str.All(x => x is >= '0' and <= '9');
[Benchmark]
public bool OldSchool()
{
if (_str.Length != N)
return false;
foreach (var c in _str)
if (c is < '0' or > '9')
return false;
return true;
}
}
Update : Regex got a little faster, linq stayed about the same
Lesson 1, This was completely unnecessary and a waste of time. Pick your favorite
Lesson 2, Don't bring linq to a jitter optimized foreach
party
Upvotes: 4