Daniele Sartori
Daniele Sartori

Reputation: 1703

How to write this Regex expression?

So i have a string in input that represent a format for a duration. This format can be of various type like : "hh:mm:ss" or "sssss" or ""hhmmss" etc...

My goal is to understand if this string, inserted by the user is a valid format. What i did until now :

Regex rgx = new Regex("[^a-zA-Z0-9 ]");
input = rgx.Replace(input, string.Empty);

At this point i have a string that should be in this format "hhmmss" or "sssss" or "mmss". I need to check if the user inserted some typo by mistake, doing a Regex.Match using this list of valid blocks

List<string> entry = new List<string> { 
  "h", "hh", "hhh", "hhhh", 
  "m", "mm", "mmm", "mmmm", "mmmmm", 
  "s", "ss", "sss", "ssss", "sssss" };

how can i do that?

EDIT: I'll try to explain some points. First of all, in this phase of my tool, i just need to check if the input from the user is valid. With valid, i mean that, any format that the user will enter (with separator or not), it is composed by at least the block of second. I am supossing that the user won't do anything like input = hsssmhhmms cause it won't have any sense. The only thing i'll concede is a typo (for instance it press "g" instead of "h"). Of course the problem it's still hard, since this blocks of data can be wrapped in a multidute of forms. That is why i did that first step, to work on a compact block.

Upvotes: 1

Views: 120

Answers (2)

RoJaIt
RoJaIt

Reputation: 461

Can you be sure that it is ordered? (h->m->s) Then it's:

var match = Regex.Match(input, "^([h]{0,4})[:]?([m]{0,5})[:]?([s]{0,5})$");
if (match.Success)
{
    string hours = match.Groups[1].Value;
    string mins = match.Groups[2].Value;
    string secs = match.Groups[3].Value;
}

If not:

var match = Regex.Match(tbName.Text, "^([h]{0,4}|[m]{0,5}|[s]{0,5})[:]?([h]{0,4}|[m]{0,5}|[s]{0,5})[:]?([h]{0,4}|[m]{0,5}|[s]{0,5})$");
if (match.Success)
{
    string hours = match.Groups.Cast<Group>().Skip(1).Select(x => x.Value).FirstOrDefault(x => x.Contains('h'));
    string mins = match.Groups.Cast<Group>().Skip(1).Select(x => x.Value).FirstOrDefault(x => x.Contains('m'));
    string secs = match.Groups.Cast<Group>().Skip(1).Select(x => x.Value).FirstOrDefault(x => x.Contains('s'));
}

You can test these Expressions here:

PS:

if you replace : with \W_ it will allow other non-word character (,;-_? etc.)

? stands for zero or one. -> you can have only one of them in between.

* allows zero or many non-word character. -> so you can have something like: "hhh_-_--,.,;;mm,ssss"

Upvotes: 1

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

Why not just try formatting? If system can format out then userFormat is a correct format:

private static bool IsValidFormat(string userFormat) {
  // Gini pig
  TimeSpan sample = new TimeSpan(1234567);

  string escaped = string.Concat(userFormat
    .Select(c => char.IsLetter(c) || c == '%'
       ? c.ToString() 
       : "\\" + c.ToString())); // delimiters like :,;+... should be escaped

  try {
    sample.ToString(escaped);

    return true;
  }
  catch (FormatException) {
    return false; 
  } 
}

...

// is this allowed? - true
Console.WriteLine(IsValidFormat(@"hh;mm;;ss+++ffff*h"));
// is this allowed? - false
Console.WriteLine(IsValidFormat(@"zzzz"));

Upvotes: 2

Related Questions