# CSV

## Read

Perigee ships with several powerful CSV tools that make reading even complicated CSV's very easy and fast with memory efficient span based data reads.

```csharp
//Get a memory stream to the file
using MemoryStream ms = new MemoryStream(File.ReadAllBytes("Products.csv"));

//Read the CSV as a list of T classes
List<Product> products = CSVReader.ReadAs<Product>(ms);

//Read the csv as a DataTable
DataTable ProductTable =  CSVReader.ToDataTable(ms, out var res);
```

{% hint style="info" %}
When using the `.ToDataTable()` methods, you get the resulting parse parameters sent back as an out variable. This allows you to check it's encoding, formatting, delimiters, header row location, etc.&#x20;
{% endhint %}

## Write

To write CSV data supply a `DataTable` and any additional handlers to override specifics on a data type.

* We'll read in the `Products.CSV` as our source.
* Let's then declare a writer, and set it to `BAR` delimited
* Register a decimal handler that write's any decimals with 3 digits after the period
  * The handler sends you back the **object** (in this case a decimal)
  * The **Row Index**.
  * The **Column Name**.
  * It expects a return value of the string converted version, as well as a `boolean` indicating if the transform was successful.
  * Any NON-SUCCESSFUL transformed handlers are added to the writers **LoadLog** property and can be viewed after conversion.
* Then simply call `.Write()`.

```csharp
//Read products CSV from above reader demo
using MemoryStream ms = new MemoryStream(File.ReadAllBytes("Products.csv"));
DataTable ProductTable = CSVReader.ToDataTable(ms, out var res);

//Write this table back out to CSV using a custom decimal format and BAR delimited
CSVWriter writer = new CSVWriter(ProductTable, (d) => "Products").WithDelimiter("|");

//You can register booleans, strings, datetimes, decimals... etc.
writer.RegisterHandler<decimal>((dec, rowIndex, colName) => new Tuple<string, bool>(dec.ToString("N3"), true));    

//Write it out
string content = writer.Write();
```

{% code title="writerResult.csv" %}

```csharp
Products
ID|PRODUCT|PRICE
1|MILK|8.990
2|55-INCH TV|899.990
3|BREAD|1.990
```

{% endcode %}

## Clean

Maybe the only thing you need to do is take the absolutely horrendous CSV data in the [Sample Data section](#sample-data) and just create a CSV that can be cleanly loaded into another system.

```csharp
using MemoryStream ms = new MemoryStream(File.ReadAllBytes("Products.csv"));

string cleanContent = CSVReader.Clean(ms);
```

This transforms the very badly formatted CSV Sample Data into this:

```csv
ID,PRODUCT,PRICE
1,MILK,8.99
2,55-INCH TV @ aisle 4,899.99
3,BREAD,1.99
```

### Sample Data

Included is the sample data and classes used above.

{% code title="Products.csv" %}

```csv
CSVData as of 1/1/2001
By: Yours Truly
ID@PRODUCT@PRICE
1@MILK@8.99
2@'55-INCH TV @ aisle 4'@899.99
3@BREAD@1.99
```

{% endcode %}

```csharp
public class Product
{
    public int id { get; set; } = 0;
    public string product { get; set; }
    public decimal price { get; set; }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.perigee.software/core-modules/file-formats/csv.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
