stuartd
stuartd

Reputation: 73253

Regex: replace period with colon in text

I need to replace a number with a decimal point with a colon. So in short:

Input

Pick me up at 5.50 and take me to the zoo

Required

Pick me up at 5:50 and take me to the zoo


However, I don't want to replace a number with just a period:

Input

Pick me up at 5. Take me to the zoo.

Required

Pick me up at 5. Take me to the zoo.

I could do this by brute force, but I believe a regex is the best solution here, and inevitably as I am not a regex expert, I have come up against Zawinski's Law.

I can match the number, but I am stuck on how to do the replacement.

\d\.\d+

enter image description here

I believe I need to use lookahead and/or grouping, but I'm not familiar with the syntax.

I've found previous questions like Replace dot(.) with comma(,) using RegEx? and Remove decimal point when not between two digits but the first advises not using a regex at all, while the second seems relevant but I can't figure out how to change the answer to get the match, let alone how to replace it.

Upvotes: 3

Views: 2556

Answers (2)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626950

To continue with your current solution, you need to make sure the digits on both ends of the dot can be left intact after the replacement, but still be accounted (checked) for when matching.

There are 2 ways to achieve that:

  • Using capturing groups + backreferences in the replacement
  • Using lookarounds

Here is the first approach with capturing groups:

Regex.Replace(s, @"(\d)\.(\d+)", "$1:$2")

And the other with lookarounds:

Regex.Replace(s, @"(?<=\d)\.(?=\d)", ":")

See the regex demo 1 and regex demo 2.

Details

  • (\d) - Group 1 (later referred to with the help of the $1 replacement backreference): any one Unicode digit or
  • (?<=\d) - a positive lookbehind that requires a digit immediately to the left of the current location
  • \. - a dot
  • (\d+) - Group 2 (later referred to with the help of the $2 replacement backreference): any 1 or more Unicode digits or
  • (?=\d) - a positive lookahead that requires a digit immediately to the right of the current location

You might go on tweaking it to only match the strings inside word boundaries with a limiting quantifier on the second \d to match only 2 digits:

\b(\d)\.(\d{2})\b

or

(?<=\b\d)\.(?=\d{2}\b)

See another regex demo (and Version 2 regex with lookarounds). Note that sometimes further word boundary adjustment is necessary.

To make sure you only match ASCII digits, use the RegexOptions.ECMAScript option. Or replace all \d with [0-9].

Upvotes: 4

CodeSmith
CodeSmith

Reputation: 3197

For 24h format, you could use this regex:

/([0-1]?[0-9]|[2][0-3]).[0-5]?[0-9]/

Basically this matches:

Hours:

  • 5, 05 etc (meaning 5 AM)
  • 12, 15 etc (for PM)
  • 20, 21, 22, 23 but not 24 or 50 (for PM)

Minutes:

  • 5 for minutes without leading zero
  • 05 for leading zeros
  • all double-digit numbers that can be minutes in an hour (00-59)
  • but not the non-minute numbers minutes like 60, 89 etc..

To make sure you do it only when the valid time is entered. It's closest you can get. Match entire regex, replace . with : and put it back.

Upvotes: 1

Related Questions