---
title: "Read a vCard (.vcf) file in C#"
url: https://recursiveai.org/onemail/cookbook/read-vcf-file/
description: "Read a .vcf vCard file in C#: names, emails, phone numbers, addresses, organization and birthdays from vCard 2.1, 3.0 and 4.0. Runnable code with OneMail.Calendar 0.3.0-preview.2."
version: "0.3.0-preview.2"
---

# Read a vCard (.vcf) file in C#

Parse the file with `ContentDocument.Parse` and read each card through `VCardDocument.Cards`: names, emails, phones, addresses and dates come back typed and decoded. vCard 2.1, 3.0 and 4.0 all read the same way. The program below reads three contacts, one in each version.

```shell
dotnet new console -o ReadVcf && cd ReadVcf
dotnet add package RecursiveAI.OneMail.Calendar --version 0.3.0-preview.2
```

Save this as `contacts.vcf`. The 2.1 card is quoted-printable UTF-8, as older phones and Outlook export it.

```text
BEGIN:VCARD
VERSION:3.0
FN:Ana Ruiz
N:Ruiz;Ana;;;
ORG:Example Corp;Sales
TITLE:Account manager
EMAIL;TYPE=INTERNET,WORK:ana.ruiz@example.com
TEL;TYPE=WORK,VOICE:+1 555 0100
TEL;TYPE=CELL:+1 555 0199
ADR;TYPE=WORK:;;100 Main St;Springfield;IL;62701;USA
BDAY:1988-04-12
END:VCARD
BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:M=C3=BCller;J=C3=BCrgen
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:J=C3=BCrgen M=C3=BCller
EMAIL;PREF;INTERNET:juergen@example.de
TEL;HOME:+49 30 1234567
END:VCARD
BEGIN:VCARD
VERSION:4.0
FN:Sam Lee
N:Lee;Sam;;;
EMAIL;TYPE=home;PREF=1:sam@example.org
TEL;VALUE=uri;TYPE="voice,cell":tel:+1-555-0142
BDAY:--0615
END:VCARD
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using RecursiveAI.OneMail.Calendar;
using RecursiveAI.OneMail.Calendar.VCard;

// vCard 2.1, 3.0 and 4.0 in one file; quoted-printable and CHARSET are decoded
ContentDocument doc = ContentDocument.Parse(File.ReadAllBytes("contacts.vcf"));

foreach (VCardView card in VCardDocument.Cards(doc))
{
    Console.WriteLine($"{card.FormattedName} (vCard {card.VersionText})");
    if (card.Name is { } n)
        Console.WriteLine($"  name:  {Join(n.Given)} / {Join(n.Family)}");
    if (card.Organization.Count > 0)
    {
        string org = string.Join(", ", card.Organization);
        Console.WriteLine($"  org:   {org}; {card.Title}");
    }
    foreach (VCardTyped email in card.Emails)
        Console.WriteLine($"  email: {email.Value} [{string.Join(",", email.Types)}]");
    foreach (VCardTyped phone in card.Phones)
        Console.WriteLine($"  phone: {phone.Value} [{string.Join(",", phone.Types)}]");
    foreach (VCardAddress adr in card.Addresses)
        Console.WriteLine($"  addr:  {Join(adr.Street)}, {Join(adr.Locality)}");
    // vCard 4.0 allows a birthday without a year
    if (card.Birthday is { } b)
    {
        string year = b.Year?.ToString() ?? "-";
        Console.WriteLine($"  bday:  {year}-{b.Month:00}-{b.Day:00}");
    }
}

static string Join(IReadOnlyList<string> parts) => string.Join(" ", parts);
```

```text
Ana Ruiz (vCard 3.0)
  name:  Ana / Ruiz
  org:   Example Corp, Sales; Account manager
  email: ana.ruiz@example.com [INTERNET,WORK]
  phone: +1 555 0100 [WORK,VOICE]
  phone: +1 555 0199 [CELL]
  addr:  100 Main St, Springfield
  bday:  1988-04-12
Jürgen Müller (vCard 2.1)
  name:  Jürgen / Müller
  email: juergen@example.de [PREF,INTERNET]
  phone: +49 30 1234567 [HOME]
Sam Lee (vCard 4.0)
  name:  Sam / Lee
  email: sam@example.org [home]
  phone: tel:+1-555-0142 [voice,cell]
  bday:  --06-15
```

The quoted-printable name decodes to “Jürgen Müller”. Type parameters are kept as written in each version (`INTERNET,WORK`, `PREF`, `voice,cell`). The 4.0 birthday has no year, which vCard 4.0 allows.

## What you get

- **Every field as a list.** `VCardName` and `VCardAddress` hold lists, because a vCard field can have several values.
- **The rest of RFC 6350:** `Photos` (inline or by URL), `Impps`, `Related`, `Categories`, `Note`, `Uid` and more.
- **Lossless.** An untouched file writes back byte for byte with `doc.Write(stream)`.
- **Outlook contacts.** `RecursiveAI.OneMail` reads contacts from MSG and PST files as `MapiContact`, and converts them to and from vCard with a loss report.

## Limits

- **A preview.** 0.3.0-preview.2: the API can change before 1.0.
- **Unchecked against Outlook.** The project lists this gap: “no Outlook-produced contact PST or MSG with the full property set, and no Outlook import/export of vCards to compare against”.
- **Phone and email slots.** Outlook’s fixed slots (business phone, mobile, email 1 to 3) map to vCard only approximately. The conversion reports each such change (`PhoneSlotChanged`, `EmailSlotChanged`).

Everything about contacts: [Contacts](https://recursiveai.org/onemail/docs/contacts/). The parser: [ICS and vCard](https://recursiveai.org/onemail/docs/calendar/).
