/ / Parse time string usando regex - regex

Parse time string usando regex - regex

La mia stringa di tempo può essere in uno dei seguenti formati (x e y - numero intero numeri, h e m - simboli):

  • xh ym
  • xh
  • ym
  • y

Esempi:

  • 1h 20m
  • 45m
  • 2h
  • 120

Quale espressione regolare dovrei scrivere per ottenere x e y numeri da tale stringa?

risposte:

3 per risposta № 1

Immagino che tu stia usando .NET a causa del tuo nome utente. :)

Penso che in questo caso sia più facile da usare TimeSpan.ParseExact per questo compito.

È possibile specificare un elenco di formati consentiti (guarda qui per il formato di questi) e ParseExact leggerà nel TimeSpan in accordo con loro.

Ecco un esempio:

var formats = new[]{"h"h"", "h"h "m"m"", "m"m"", "%m"};
// I have assumed that a single number means minutes

foreach (var item in new[]{"23","1h 45m","1h","45m"})
{
TimeSpan timespan;
if (TimeSpan.TryParseExact(item, formats, CultureInfo.InvariantCulture, out timespan))
{
// valid
Console.WriteLine(timespan);
}
}

Produzione:

00:23:00
01:45:00
01:00:00
00:45:00

L'unico problema è che è piuttosto inflessibile. Gli spazi bianchi aggiuntivi nel mezzo non riusciranno a convalidare. Una soluzione più robusta che utilizza Regex è:

var items = new[]{"23","1h 45m", "45m", "1h", "1h 45", "1h   45", "1h45m"};
foreach (var item in items)
{
var match = Regex.Match(item, @"^(?=d)((?<hours>d+)h)?s*((?<minutes>d+)m?)?$", RegexOptions.ExplicitCapture);
if (match.Success)
{
int hours;
int.TryParse(match.Groups["hours"].Value, out hours); // hours == 0 on failure

int minutes;
int.TryParse(match.Groups["minutes"].Value, out minutes);

Console.WriteLine(new TimeSpan(0, hours, minutes, 0));
}
}

Ripartizione della regex:

  • ^ - inizio della stringa
  • (?=d) - deve iniziare con una cifra (fai questo perché entrambe le parti sono contrassegnate come facoltative, ma vogliamo assicurarci che almeno una sia presente)
  • ((?<hours>d+)h)? - ore (facoltativo, acquisizione in un gruppo con nome)
  • s* - spazio bianco (facoltativo)
  • ((?<minutes>d+)m?)? - minuti (opzionale, cattura nel gruppo con nome, anche la "m" è opzionale)
  • $ - fine della stringa

4 per risposta № 2
(d+)([mh]?)(?:s+(d+)m)?

È quindi possibile ispezionare i gruppi 1-3. Per i tuoi esempi quelli sarebbero

("1", "h", "20")
("45", "m", "")
("2", "h", "")
("120", "", "")

Come sempre, potresti voler usare delle ancore ^, $, b...


1 per risposta № 3

Direi che la soluzione di mhyfritz è semplice, efficiente e buona se il tuo contributo è solo ciò che hai mostrato. Se hai mai avuto bisogno di gestire casi d'angolo, puoi usare un'espressione più discriminante:

^(d+)(?:(h)(?:s+(d+)(m))?|(m?))$

Ma può essere eccessivo ...

(sbarazzarsi di ^ e $ se è necessario rilevare tale modello in un corpo di testo più grande, ovviamente).


1 per risposta № 4

Prova questo: ^(?:(d+)hs*)?(?:(d+)m?)?$:

var s = new[] { "1h 20m", "45m", "2h", "120", "1m 20m" };

foreach (var ss in s)
{
var m = Regex.Match(ss, @"^(?:(d+)hs*)?(?:(d+)m?)?$");

int hour = m.Groups[1].Value == "" ? 0 : int.Parse(m.Groups[1].Value);
int min = m.Groups[2].Value == "" ? 0 : int.Parse(m.Groups[2].Value);

if (hour != 0 || min != 0)
Console.WriteLine("Hours: " + hour + ", Mins: " + min);
else
Console.WriteLine("No match!");
}

0 per risposta № 5

in bash

echo $string | awk "{for(i=1;i<=NF;i++) print $i}" | sed s/[hm]/""/g