I'm making a console app in which I'll ask the user for a phone number int main_phone, but if he types a string thats not convertible to int such as "Caramel" I want to give him a warning and ask for a number.
Well I thought a recursive function would do, but I'm lacking logical aptitude for this one. What I came up with was the following code snippet:
static int? TryParse(string str)
{
int tmp;
if (int.TryParse(str, out tmp)) {
return tmp;
}
return null;
}//End of TryParse();
static int? verifyParse(string str, string lable)
{
int? testedVar = TryParse(str);
if (testedVar == null)
{
Console.WriteLine("Este campo pode apenas conter dígitos de 0 à 9.");
Console.Write(lable);
verifyParse(Console.ReadLine(), lable);
return null;
}
else
{
return testedVar;
}
}//End of verifyParse();
Console.Write("Phone(main):");
int? main_phone = verifyParse(Console.ReadLine(),"Telefone (Principal):");
The problem is that if the user inputs "string" main_phone will comeback as null, then it asks for a number between 0 and 9, but even if the user passes a convertible string , such as "12", main_phone will still be null.
How can I fix that?
EDIT 1:
What I really need is a function that if the string can be converted to an int it returns the int, else it gives me an error and asks me to input something between 0 and 9. I find this hard because the function would have to be able to return an int or run it self all over again(to require and test a valid input), but it can't because either it returns the int or it doesn't return nothing.
EDIT 2:
Here is a little update on my problem:
static int TryParse(string lable)
{
string str = Console.ReadLine();
Regex regex = new Regex(#"\d+");
while (!regex.IsMatch(str))
{
Console.WriteLine("Insira apenas dígitos de 0-9, por favor.");
Console.Write(lable);
str = Console.ReadLine();
}
return Convert.ToInt32(str);
}//End of TryParse();
Console.Write("Telefone (Principal):");
int main_phone = TryParse("Telefone (Principal):");
It works really well, except when I input something like "1a". In that case I'll get this error:
System.FormatException: Input string was not in a correct format.
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at System.Convert.ToInt32(String value)
at App1.Program.TryParse(String lable)
at App1.Program.Main(String[] args)
Here's a solution that loops until a good match is produced, using regex validation
Regex regex = new Regex(#"\d{3}-?\d{3}-?\d{4}");
string phonenumber = Console.ReadLine();
while (!regex.IsMatch(phonenumber))
{
Console.WriteLine("Bad Input");
phonenumber = Console.ReadLine();
}
return Convert.ToInt32(phonenumber.Replace("-",""));
which will match 5555555555 and 555-555-5555, and return the int value.
How about you just read in as a string what the user puts in, replace all "-" with "", then check if it numeric and check length. No need to make a recursive method for such a simple task.
how about this
[VB.NET]
Function CheckPhoneNumber(ByVal Number As String) As String
Number = Number.Replace("-","")
Number = Number.Replace("(","")
Number = Number.Replace(")","")
If Not IsNumeric(Number) Then
Return "Error"
Else
Return Number
End If
End Function
[C#]
public string CheckPhoneNumber(string Number)
{
Number = Number.Replace("-", "");
Number = Number.Replace("(", "");
Number = Number.Replace(")", "");
if (!Information.IsNumeric(Number)) {
return "Error";
} else {
return Number;
}
}
What about just using int.TryParse?
void Main()
{
var input = Console.ReadLine();
int phone;
while(!int.TryParse(input, out phone)){
Console.WriteLine("Please enter a number");
input = Console.ReadLine();
}
Console.WriteLine("Success");
}
To cleanup the input, you can replace the "-"
var input = Console.ReadLine().Replace("-", "");
Could strip out any extra characters ('+', '-', '#', '(', ')', and '*') then try
bool successful;
int result;
if(!int.tryparse(string, result))
{
Console.WriteLine("Invalid phone number");
}
else
{
Console.WriteLine("Valid phone number");
}
Or whatever you like.
Of course the alternative is to use REGEX. (note: I'm stealing this from Regex Hero found here: http://regexhero.net/tester/)
string strRegex = #"[+]?[\d][\d\s()-.]{8,}[\d]";
RegexOptions myRegexOptions = RegexOptions.None;
Regex myRegex = new Regex(strRegex, myRegexOptions);
foreach (Match myMatch in myRegex.Matches(strTargetString))
{
if (myMatch.Success)
{
// Add your code here
}
}
Try this modified version of your code segment:
static int? verifyParse(string str, string lable)
{
int? testedVar = TryParse(str);
if (testedVar == null)
{
Console.WriteLine("Este campo pode apenas conter dígitos de 0 à 9.");
Console.Write(lable);
int? newVal = verifyParse(Console.ReadLine(), lable);
if (newVal != null)
return newVal;
else
return null;
}
else
{
return testedVar;
}
}//End of verifyParse();
Related
I'm making a program that reverses a string and doesn't allow for anything else than letters and whitespaces, problem is that if i enter a non-valid input and then try to input a valid input it just keeps printing error. I think the problem has something to do with my while loop and the bool result, but i can't figure it out. Please help and thank you!
static void Reverse()
{
string name;
Console.Write("Enter your name: ");
name = Console.ReadLine();
bool result = name.All(c => char.IsWhiteSpace(c) || char.IsLetter(c));
if (Regex.IsMatch(name, #"^[a-zA-Z- ]+$")) // Validates the input for characters and/or spaces
{
char[] charArr = name.ToCharArray();
Array.Reverse(charArr);
string nameRev = new string(charArr);
Console.WriteLine("String is {0}", nameRev);
}
else
{
while (name == String.Empty || result == false) //Should validate the input for whitespace or letter if it doesn't pass the first validation
{
Console.Write("Error! Enter your name, only letters allowed: ");
name = Console.ReadLine();
}
}
You need to wrap your while loop around the hole sequence instead of just having it inside the else statement.
Example:
static void Reverse()
{
// Continues executing as long as result stays false.
bool result;
do
{
string name;
Console.Write("Enter your name: ");
name = Console.ReadLine();
result = name.All(c => char.IsWhiteSpace(c) || char.IsLetter(c));
if (Regex.IsMatch(name, #"^[a-zA-Z- ]+$"))
{
char[] charArr = name.ToCharArray();
Array.Reverse(charArr);
string nameRev = new string(charArr);
Console.WriteLine("String is {0}", nameRev);
}
else
{
Console.WriteLine("Error! Only letters allowed");
}
}
while (!result);
}
I'm trying to use a method to validate a string that has two capital letters at the start and 3 digits after that. The one I am writing at the minute is:
static void Main(string[] args)
{
Console.WriteLine("Enter stock quantity:");
string qty = Console.ReadLine();
string code = enterStockCode();
}
static string enterStockCode()
{
string[] pLetters = new string[] { "BL", "FS", "HJ", "PX" };
bool isValid = false;
while (isValid == false)
{
Console.WriteLine("Enter stockcode:");
string stockCode = Console.ReadLine();
char[] stockCodeSplit = stockCode.ToCharArray();
var vLetters = (stockCodeSplit[0] + stockCodeSplit[1]);
var vNumbers = (stockCodeSplit[2] + stockCodeSplit[3] + kCodeSplit[4]);
// I'd like to know if there is a better way of splitting this string
// by using regular expressions or another way.
foreach (var Letter in pLetters)
{
if (vNumbers.ToString() == Letter)
{
if (Regex.Match(vNumbers.ToString(), #"^\d[0-9]{2}$").Success)
return stockCode;
Console.WriteLine("Invalid stock code");
}
}
}
return "Invalid";
}
(Also I would like to know if the "2" in:
#"^\d[0-9]{2}$"
means 2 times or three times as c# uses index of 0.)
Any help at all is appreciated.
please try:
^[A-Z]{2}\d{3}
If you just want to find an expression starting with 2 capital letters followed by 3 digits you should not put an ending limiter, just the starting.
The 2 means 2 times.
This question already has answers here:
what's the quickest way to extract a 5 digit number from a string in c#
(8 answers)
Closed 9 years ago.
What's the best way to extract the number part from this string? I looked at RegularExpressions but they confuse the hell out of me. Is it possible with SubString?
/store/457987680928164?id=2
All I require is the numbers.
RegEx is a good way to go with this problem, but if you're set on using SubString...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string test = "/store/457987680928164?id=2";
int start = test.IndexOfAny("0123456789".ToCharArray());
int end = test.IndexOf("?");
Console.WriteLine(test.Substring(start, end - start));
Console.ReadLine();
}
}
}
You should get RadExpression Designer and teach yourself RegEx's with the cheat sheets.
If RegEx's frighten you, simply do it in a loop which will often be quicker:
string s = "/store/457987680928164?id=2";
string numericInput = string.Empty;
foreach(char c in s)
{
if (char.IsDigit(c))
numericInput += c;
}
I'd prefer Regex, this isn't using substring runs faster than it in my examples
static long GrabFirstLongFromString(string input)
{
string intAsString = String.Empty;
bool startedInt = false;
foreach(char c in input)
{
if (Char.IsDigit(c))
{
startedInt = true; //really only care about the first digit
intAsString += c;
}
else if (startedInt)
return long.Parse(intAsString);
}
return -1; //define a default, since this only does a 0 or positive I picked a negative
}
This does it with regular expressions
private static Regex digitsOnly = new Regex(#"[^\d]");
public static string RemoveNonNumbers(string input)
{
return digitsOnly.Replace(input, "");
}
Or just simple Regex:
Regex r = new Regex(#"\d+");
MatchCollection m = r.Matches("/store/457987680928164?id=2");
if (m.Count > 0)
{
Console.WriteLine(string.Format("Big number: {0} - Little number: {1}", m[0], m[1]));
}
The above prints:
Big number: 457987680928164 - Little number: 2
I would definitely recommend using RegEx for this. String pattern matching and extraction is really an ideal scenario for Regular Expressions.
Here is a RegEx that will match the string sample you provided, with capturing parenthesis for the numeric parts of the String:
^/store/(\d+)\?id=(\d+)
You can verify it here in the Regex Tester. I tested it using your sample String and the RegEx I wrote above.
I came up with these extension methods to simplify common string parsing tasks:
private static string Substring(string str, string value, bool isLastValue, bool isAfterValue, StringComparison comparisonType, string defaultValue)
{
int pos = isLastValue ? str.LastIndexOf(value, comparisonType) : str.IndexOf(value, comparisonType);
if (pos == -1) return defaultValue;
return isAfterValue ? str.Substring(pos + value.Length) : str.Substring(0, pos);
}
public static string SubstringBeforeFirst(this string str, string value, StringComparison comparisonType = StringComparison.CurrentCulture, string defaultValue = "")
{
return Substring(str, value, false, false, comparisonType, defaultValue);
}
public static string SubstringBeforeLast(this string str, string value, StringComparison comparisonType = StringComparison.CurrentCulture, string defaultValue = "")
{
return Substring(str, value, true, false, comparisonType, defaultValue);
}
public static string SubstringAfterFirst(this string str, string value, StringComparison comparisonType = StringComparison.CurrentCulture, string defaultValue = "")
{
return Substring(str, value, false, true, comparisonType, defaultValue);
}
public static string SubstringAfterLast(this string str, string value, StringComparison comparisonType = StringComparison.CurrentCulture, string defaultValue = "")
{
return Substring(str, value, true, true, comparisonType, defaultValue);
}
As for getting the number from your example:
string s = "/store/457987680928164?id=2";
string number = s.SubstringAfterLast("/").SubstringBeforeFirst("?");
YARS (Yet Another Regex Solution)
The following:
var str = "/store/457987680928164?id=2";
var regex = new Regex(#"\d+");
foreach (Match match in regex.Matches(str))
{
Console.WriteLine(match.Value);
}
outputs this:
457987680928164
2
string str = "/store/457987680928164?id=2";
string num = new string(str.Remove(str.IndexOf("?")).Where(a => char.IsDigit(a)).ToArray());
I am trying to display my results as follows:
-.-|- [tab] kt
-.|-- [tab] nm
-.|-|- [tab] ntt
But this is my current output
-.-|-| kt
-.|--| nm
-.|-|-| [tab]ntt
There is a | at the end of every Morse code which I would like to remove since it is at the end.
Also because the user can input Morse code with space between dots and dashes - i noticed that it affects the alignment of the characters and not all of them get tabbed properly. The word tab isn't supposed to show i just wrote it in because I didn't know how to place a real tab.
private static readonly IDictionary<char, string> morseCode_alpha = new Dictionary<char, string>
{
{'a', ".-"},{'b',"-..."}, {'c',"-.-."}, {'d',"-.."}, {'e',"."},
{'f',"..-."}, {'g',"--."}, {'h',"...."},{'i',".."}, {'j',".---"},
{'k',"-.-"}, {'l',".-.."}, {'m',"--"}, {'n',"-."}, {'o',"---"},
{'p',".--."}, {'q',"--.-"}, {'r',".-."}, {'s',"..."}, {'t',"-"},
{'u',"..-"}, {'v',"...-"}, {'w',".--"}, {'x',"-..-"}, {'y',"-.--"}, {'z',"--.."}
};
private static string ConvertMorseToText(string symbolCode)
{
var builder = new StringBuilder(4 * symbolCode.Length);
foreach (char c in symbolCode)
builder.Append(morseCode_alpha[c]);
return builder.ToString();
}
private static string ConvertTextToMorse(char ch)
{
if (morseCode_alpha.Keys.Contains(ch))
return morseCode_alpha[ch];
else
return string.Empty;
}
private static string ConvertStringToMorse(string letters)
{
StringBuilder sb = new StringBuilder();
foreach (char ch in letters)
{
if (sb.Length != 0 && sb[sb.Length - 1] != ' ')
sb.Append("|");
sb.Append(ConvertTextToMorse(ch));
}
return sb.ToString();
}
private static IEnumerable<string> Permutations( string symbolCode)
{
int n = symbolCode.Length;
if (n == 0 || symbolCode.Length == 0)
yield return " ";
else
foreach (var entry in morseCode_alpha)
if (symbolCode.StartsWith(entry.Value))
foreach (string next in Permutations(symbolCode.Substring(entry.Value.Length)))
yield return entry.Key + next;
}
private static void Write( string rest)
{
string result = ConvertStringToMorse(rest);
Console.Write(result+"\t");
Console.WriteLine(rest);
}
static void Main(string[] args)
{
string morseInput;
string entered = "";
do
{
Console.WriteLine("Enter Morse Code: \n");
morseInput = Console.ReadLine().Replace(" ","");
bool isValid = Regex.IsMatch(morseInput, #"^[-.]+$");
if (isValid)
{
Console.WriteLine("\nAll permutations:\n");
string morse = ConvertMorseToText(entered);
string permutations = morseInput.Substring(morse.Length);
Write(permutations);
var nexts = new List<string>(Permutations(permutations));
foreach (string next in nexts)
Write(next);
}
else
{
Console.WriteLine("\nFormat of morse must be only dots and dashes.");
Console.WriteLine("Parameter name: "+morseInput+"\n");
}
}
while (morseInput.Length != 0);
}
And, to answer the other part of the question...
Tabstops are fixed for console writing, so it would be better to use something like String.PadRight
so, your code could be:
private static void Write(string rest)
{
string result = ConvertStringToMorse(rest);
Console.Write(result.PadRight(20));
Console.WriteLine(rest);
}
Draft version of the method:
private static string ConvertStringToMorse(string letters)
{
var result = string.Join("|",
letters
.Select(ConvertTextToMorse)
.Where(morse => !string.IsNullOrEmpty(morse)));
return result;
}
Update:
Please note that the entered variable is used only once: when defined - empty string is assigned. Then the ConvertMorseToText(entered) method is called: it always returns empty string for the empty string argument. After this assignment string permutations = morseInput.Substring(morse.Length); the permutations variable will store exactly the same value as morse variable (because morse.Length is always 0).
So, it seems that the entered variable and the ConvertMorseToText() method are useless (both can be safely removed):
static void Main(string[] args)
{
do
{
Console.WriteLine("Enter Morse Code: ");
string morseInput = Console.ReadLine();
if (string.IsNullOrWhiteSpace(morseInput))
{
// Empty or consists only of white-space characters
break;
}
morseInput = morseInput.Replace(" ", "");
bool isValid = Regex.IsMatch(morseInput, #"^[-.]+$");
if (isValid)
{
Console.WriteLine("All permutations:");
Console.WriteLine();
var nexts = Permutations(morseInput).ToList();
foreach (string next in nexts)
Write(next);
}
else
{
Console.WriteLine();
Console.WriteLine("Format of morse must be only dots and dashes.");
Console.WriteLine("Parameter name: {0}", morseInput);
}
}
while (true);
}
Update 2:
Consider using TryGetValue() method of Dictionary<TKey, TValue> instead of Keys.Contains and [] (indexer) i.e. do not perform look-up twice:
private static string ConvertTextToMorse(char ch)
{
string result;
return morseCode_alpha.TryGetValue(ch, out result) ? result : string.Empty;
}
Instead this code:
Console.Write(result+"\t");
Console.WriteLine(rest);
Use
Console.WriteLine("{0,-10}{1,-10}", result, rest);
Then you will see two columns (each max 10 charachters) with left alignment. Or remove "-" sign if you want right alignment.
I wonder what the best practice for parsing and validating a mobile number before sending a text is. I've got code that works, but I'd like to find out better ways of doing it (as my last question, this is part of my early new years resolution to write better quality code!).
At the moment we are very forgiving when the user enters the number on the form, they can enter things like "+44 123 4567890", "00441234567890", "0123456789", "+44(0)123456789", "012-345-6789" or even "haven't got a phone".
However, to send the text the format must be 44xxxxxxxxxx (this is for UK mobiles only), so we need to parse it and validate it before we can send. Below is the code that I have for now (C#, asp.net), it would be great if anyone had any ideas on how to improve it.
Thanks,
Annelie
private bool IsMobileNumberValid(string mobileNumber)
{
// parse the number
_mobileNumber = ParsedMobileNumber(mobileNumber);
// check if it's the right length
if (_mobileNumber.Length != 12)
{
return false;
}
// check if it contains non-numeric characters
if(!Regex.IsMatch(_mobileNumber, #"^[-+]?[0-9]*\.?[0-9]+$"))
{
return false;
}
return true;
}
private string ParsedMobileNumber(string number)
{
number = number.Replace("+", "");
number = number.Replace(".", "");
number = number.Replace(" ", "");
number = number.Replace("-", "");
number = number.Replace("/", "");
number = number.Replace("(", "");
number = number.Replace(")", "");
number = number.Trim(new char[] { '0' });
if (!number.StartsWith("44"))
{
number = "44" + number;
}
return number;
}
EDIT
Here's what I ended up with:
private bool IsMobileNumberValid(string mobileNumber)
{
// remove all non-numeric characters
_mobileNumber = CleanNumber(mobileNumber);
// trim any leading zeros
_mobileNumber = _mobileNumber.TrimStart(new char[] { '0' });
// check for this in case they've entered 44 (0)xxxxxxxxx or similar
if (_mobileNumber.StartsWith("440"))
{
_mobileNumber = _mobileNumber.Remove(2, 1);
}
// add country code if they haven't entered it
if (!_mobileNumber.StartsWith("44"))
{
_mobileNumber = "44" + _mobileNumber;
}
// check if it's the right length
if (_mobileNumber.Length != 12)
{
return false;
}
return true;
}
private string CleanNumber(string phone)
{
Regex digitsOnly = new Regex(#"[^\d]");
return digitsOnly.Replace(phone, "");
}
Use a regular expression to remove any non-numeric characters instead of trying to guess how a person will enter their number - this will remove all your Replace() and Trim() methods, unless you really need to trim a leading zero.
string CleanPhone(string phone)
{
Regex digitsOnly = new Regex(#"[^\d]");
return digitsOnly.Replace(phone, "");
}
Alternatively, I would recommend you use a masked textbox to collect the # (there are many options available) to allow only numeric input, and display the input with whatever format you'd like. This way you're guaranteeing that the value received will be all numeric characters.
Check out QAS, it's a commercial solution.
They have email, phone and address validations.
http://www.qas.com/phone-number-validation-web-service.htm
We use their services for Address and Email (not phone) and have been satisfied with it.
#annelie maybe you can update your regular expression to a more powerful one. Check out this site here. It contains many expressions but I think one of the top 2 expressions in the site should be suitable to you.
public class PhoneNumber
{
public PhoneNumber(string value)
{
if (String.IsNullOrEmpty(value))
throw new ArgumentNullException("numberString", Properties.Resources.PhoneNumberIsNullOrEmpty);
var match = new Regex(#"\+(\w+) \((\w+)\) (\w+)", RegexOptions.Compiled).Match(value);
if (match.Success)
{
ushort countryCode = 0;
ushort localCode = 0;
int number = 0;
if (UInt16.TryParse(match.Result("$1"), out countryCode) &&
UInt16.TryParse(match.Result("$2"), out localCode) &&
Int32.TryParse(match.Result("$3"), out number))
{
this.CountryCode = countryCode;
this.LocalCode = localCode;
this.Number = number;
}
}
else
{
throw new ArgumentNullException("numberString", Properties.Resources.PhoneNumberInvalid);
}
}
public PhoneNumber(int countryCode, int localCode, int number)
{
if (countryCode == 0)
throw new ArgumentOutOfRangeException("countryCode", Properties.Resources.PhoneNumberIsNullOrEmpty);
else if (localCode == 0)
throw new ArgumentOutOfRangeException("localCode", Properties.Resources.PhoneNumberIsNullOrEmpty);
else if (number == 0)
throw new ArgumentOutOfRangeException("number", Properties.Resources.PhoneNumberIsNullOrEmpty);
this.CountryCode = countryCode;
this.LocalCode = localCode;
this.Number = number;
}
public int CountryCode { get; set; }
public int LocalCode { get; set; }
public int Number { get; set; }
public override string ToString()
{
return String.Format(System.Globalization.CultureInfo.CurrentCulture, "+{0} ({1}) {2}", CountryCode, LocalCode, Number);
}
public static bool Validate(string value)
{
return new Regex(#"\+\w+ \(\w+\) \w+", RegexOptions.Compiled).IsMatch(value);
}
public static bool Validate(string countryCode, string localCode, string number, out PhoneNumber phoneNumber)
{
var valid = false;
phoneNumber = null;
try
{
ushort uCountryCode = 0;
ushort uLocalCode = 0;
int iNumber = 0;
// match only if all three numbers have been parsed successfully
valid = UInt16.TryParse(countryCode, out uCountryCode) &&
UInt16.TryParse(localCode, out uLocalCode) &&
Int32.TryParse(number, out iNumber);
if (valid)
phoneNumber = new PhoneNumber(uCountryCode, uLocalCode, iNumber);
}
catch (ArgumentException)
{
// still not match
}
return valid;
}
}