The FSharp.Text.RegexProvider project contains a type provider for regular expressions.
This example demonstrates the use of the type provider:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
|
// reference the type provider dll
#r "FSharp.Text.RegexProvider.dll"
open System.Globalization
open FSharp.Text.RegexProvider
// Let the type provider do its work
type PhoneRegex = Regex< @"(?<AreaCode>^\d{3})-(?<PhoneNumber>\d{3}-\d{4}$)" >
// now you have typed access to the regex groups and you can browse it via Intellisense
PhoneRegex().TypedMatch("425-123-2345").AreaCode.Value
val it : string = "425"
// you can also get an option for simpler handling in case the rege doesn't match
match PhoneRegex().TryTypedMatch("425-123-2345") with
| Some m -> printfn "Phone number is %s" m.PhoneNumber.Value
| None -> printfn "Phone number unavailable"
|
Note that since version 1.0, generated methods are prefixed by Typed
by default.
You can disable this behaviour using the parameter noMethodPrefix
:
1:
2:
3:
4:
5:
6:
7:
8:
|
type MultiplePhoneRegex = Regex< @"\b(?<AreaCode>\d{3})-(?<PhoneNumber>\d{3}-\d{4})\b", noMethodPrefix = true >
// now the generated types are just added as an overload of the existing method name on the `Regex` type
MultiplePhoneRegex().Matches("425-123-2345, 426-123-2346, 427-123-2347")
|> Seq.map (fun x -> x.AreaCode.Value)
|> List.ofSeq
val it : string list = ["425"; "426"; "427"]
|
To ease the conversion of matched groups to primitives types, the assembly provides
the FSharp.Text.RegexExtensions module. It contains extensions methods to convert matched groups
to int
, uint32
, int64
, uint64
, int16
, uint16
, byte
, sbyte
,
float
, single
, decimal
, DateTime
, DateTimeOffset
, TimeSpan
, bool
and char
:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
|
open FSharp.Text.RegexExtensions
type TempRegex = Regex< @"^(?<Temperature>[\d\.]+)\s*°C$", noMethodPrefix = true >
// the simplest extension gets an option string depending on Success
TempRegex().Match("21.3°C").Temperature.TryValue
val it : string option = Some "21.3"
// conversion to decimal in the happy case
TempRegex().Match("21.3°C").Temperature.AsDecimal
val it : decimal = 21.3M
// When not sure whether the match occured or the format is always correct use TryAs...
// here it's successful
TempRegex().Match("21.3°C").Temperature.TryAsDecimal
val it : decimal option = Some 21.3M
// here it returns None because it's not °C, so it doesn't match
TempRegex().Match("21.3°F").Temperature.TryAsDecimal
val it : decimal option = None
// here it matches, but it is not a valid decimal
TempRegex().Match("21.3.5°F").Temperature.TryAsDecimal
val it : decimal option = None
type DateRegex = Regex< @"^Date:\s*(?<Date>\d{4}-\d{2}-\d{2})$", noMethodPrefix = true >
// for dates, specify a format, especially for local or utc conversion
DateRegex().Match("Date: 2019-06-18").Date.AsDateTime(DateTimeStyles.AssumeUniversal|||DateTimeStyles.AdjustToUniversal)
val it : System.DateTime = 18/06/2019 00:00:00
|
TypedReplace
can be used with a match evaluator that takes a MatchType as an input.
Here, the type of m is NumberRegex.MatchType
, and has a Number property infered from the regular expression :
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
|
type NumberRegex = Regex< @"(?<Number>\d+)" >
let song = "
1, 2 buckle my shoe
3, 4 knock on the door
5, 6 pick up sticks
9, 10 a big fat hen."
let numbers =
Map.ofList
[ 1, "one"; 2, "two"; 3, "three"; 4, "four"; 5, "five"
6, "six"; 7, "seven"; 8, "eight"; 9, "nine"; 10, "ten"]
// using typed replace, you can use typed match in the provided function
NumberRegex().TypedReplace(song, fun m -> numbers.[m.Number.AsInt] )
val it : string =
"
one, two buckle my shoe
three, four knock on the door
five, six pick up sticks
nine, ten a big fat hen."
|
The project is hosted on GitHub where you can report issues, fork
the project and submit pull requests. If you're adding new public API, please also
consider adding samples that can be turned into a documentation. You might
also want to read library design notes to understand how it works.
The library is available under Public Domain license, which allows modification and
redistribution for both commercial and non-commercial purposes. For more information see the
License file in the GitHub repository.