Recursive AI

Extract phone numbers from text in C#

OneContact is a free, open-source contact data library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies, no network access.

Find phone numbers inside a message and return each number with its original text and character range.

Terminal
dotnet add package RecursiveAI.OneContact

Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.

C#
using RecursiveAI.OneContact;
using RecursiveAI.OneContact.Phones;
var text = "Call (202) 555-0123.";
foreach (var match in PhoneNumber.FindAll(
    text, Region.US,
    PhoneMatchLeniency.Valid))
    Console.WriteLine(match.Number.E164);
Text
+12025550123

Possible accepts plausible length; Valid adds numbering-metadata and surrounding-text checks; ExactGrouping also requires the documented digit grouping.

Unzip the example, then dotnet run.

Full program

All sample input is written directly in the program.

C#
using RecursiveAI.OneContact;
using RecursiveAI.OneContact.Phones;
 
var text = "Call (202) 555-0123 or +44 20 7946 0123.";
foreach (var match in PhoneNumber.FindAll(text, Region.US, PhoneMatchLeniency.Valid))
{
    Console.WriteLine($"Raw: {match.RawString}");
    Console.WriteLine($"E.164: {match.Number.E164}");
    Console.WriteLine($"Span: {match.Start}..{match.End}");
}

dotnet run prints:

Text
Raw: (202) 555-0123
E.164: +12025550123
Span: 5..19
Raw: +44 20 7946 0123
E.164: +442079460123
Span: 23..39

A national number needs the correct default region; candidates that do not satisfy the chosen leniency are skipped rather than returned as valid matches.

Variations

Require exact digit grouping from the numbering metadata:

C#
using RecursiveAI.OneContact;
using RecursiveAI.OneContact.Phones;
var text = "Call +44 20 7946 0123.";
foreach (var match in PhoneNumber.FindAll(
    text, Region.US, PhoneMatchLeniency.ExactGrouping))
    Console.WriteLine(match.RawString);
Text
+44 20 7946 0123

The short answer

Use PhoneNumber.FindAll(text, region, leniency) to find phone numbers in running text. Each result contains RawString, Number, Start and End. The snippet at the top prints one E.164 number. The full program extracts both the US and British numbers from a single synthetic sentence and prints their source ranges.

This API is part of the core RecursiveAI.OneContact package. You do not need the HTML extraction package to scan a string for phone numbers. The phone matcher notes describe the candidate shapes and the acceptance rules used by the matcher.

How do I extract phone numbers from text?

Supply a region for national input and enumerate the matches. In the example, (202) 555-0123 needs US context; +44 20 7946 0123 carries its international calling code. The default region is context for interpreting national digits, not an instruction to rewrite every match into that region.

Keep the raw text if you need to show what was found. Keep Number.E164 if a downstream system needs a normalized dialing representation. Those are different outputs: the raw value carries punctuation from the source, while E.164 gives the international number without an extension. An extension remains available on the parsed number.

Start is the beginning of the match and End is the position immediately after it. These offsets let an application highlight the original substring without searching for the formatted number, which may never appear literally in the input. The full program’s first range is 5..19; it covers exactly the national number in the sentence, not the word Call or the following separator.

How can I validate phone numbers in C#?

For extraction, choose PhoneMatchLeniency.Valid when candidates must satisfy the metadata’s validity rules and surrounding-text checks. Possible accepts a parsable candidate with plausible length. StrictGrouping and ExactGrouping add increasingly specific requirements about how digit groups are separated. The variation demonstrates the last option on a British number.

A stricter choice can omit a number a person wrote in an unusual grouping. That is a tradeoff in what the extractor accepts, not a confirmation that omitted text is not a phone number. Pick a level that fits whether the application is suggesting candidates for review or importing into a stricter field.

A metadata-valid match does not prove the line is assigned, reachable or owned by the person named nearby. Parsing and matching concern the text and numbering rules. The validation recipe shows the validity properties independently of text extraction.

How can I extract a number from a string in C#?

If the intended value is a phone number, use the matcher rather than collecting every digit in the string. Removing non-digits can join unrelated values, strip an extension’s boundary and lose the plus sign that supplies international context. It also gives no source ranges for separate matches.

The matcher rejects candidate shapes resembling slash-separated dates, timestamps, page ranges and unbalanced brackets. That filtering is part of its documented behavior, not a guarantee that arbitrary prose can never produce a false match. Review extracted candidates when the surrounding text matters. For records containing names and other contact fields, the extraction documentation covers the separate rules-based contact extractor.

All onecontact recipes