Back to Guides
GuidePublished July 24, 20268 min read

How to Read and Write CSV Files in MATLAB

Read and write CSV files in MATLAB with readmatrix, readtable, writematrix, and writetable. Handle headers, missing values, validation, and append mode.

Use readmatrix and writematrix when a CSV contains numeric data. Use readtable and writetable when column names or mixed data types are part of the file contract.

TaskFunctionBest fit
Read numeric datareadmatrixNumeric rows with a header that can be skipped
Read named or mixed columnsreadtableHeaders, IDs, dates, or mixed data types
Write a numeric arraywritematrixNumeric output and appended batches
Write a tablewritetableOutput whose variable names should become headers
Write mixed valueswritecellA manually assembled header or mixed text and numbers
Control a custom text formatfopenIrregular records, explicit formatting, or encoding control
Maintain an older scriptcsvread, csvwriteLegacy code that has not moved to the newer functions

A spreadsheet is efficient for inspecting one file. Repeated test runs need the same missing-data rules, calculations, plots, and output columns every time. MATLAB is a good fit when that work already belongs in an .m analysis that engineers can review and rerun. Python with pandas can build the same pipeline; the practical reason to use MATLAB is continuity with the equations, plots, and scripts the team already maintains.

This guide develops one such pipeline around a synthetic pump-test export. The script preserves an unavailable pressure reading, rejects an unexpected file shape, calculates pressure rise from complete records, and writes a checked output file. The downloadable workspace uses MATLAB-syntax code and was executed with the RunMat runtime.

The pump-test result

The sample represents one short pump run. A test bench records elapsed time, inlet pressure, outlet pressure, and flow every half-second:

elapsed_s,inlet_pressure_kpa,outlet_pressure_kpa,flow_l_min
0.0,101.2,142.1,18.4
0.5,101.3,143.0,18.7
1.0,101.2,144.4,18.9
...
3.0,101.4,NA,19.6
3.5,101.5,149.4,19.7

The NA at three seconds is deliberate. Converting it to zero would manufacture a pressure drop. Removing only that outlet-pressure value would shift the remaining measurements out of alignment. The analysis must remove the entire record before calculating pressure rise:

pressure_rise_kpa = outlet_pressure_kpa - inlet_pressure_kpa;

The code fragments below develop the same script. Open the complete workspace when you want to run pump_test_analysis.m with pump_test.csv already loaded beside it.

Import the data without hiding the gap

Every populated field below the header should be numeric, so readmatrix is the shortest path. NumHeaderLines skips the column names, while TreatAsMissing converts NA to NaN without changing a measured zero.

This first checkpoint is self-contained. It reads the sample, checks the expected dimensions, and reports the missing outlet-pressure row.

data = readmatrix("pump_test.csv", ...
    "NumHeaderLines", 1, ...
    "TreatAsMissing", "NA");

if size(data, 2) ~= 4 || size(data, 1) == 0
    error("pump_test.csv does not match the expected four-column export");
end

missing_outlet_rows = find(isnan(data(:, 3)));
fprintf("Imported size: %d x %d\n", size(data, 1), size(data, 2));
fprintf("Missing outlet-pressure row: %d\n", missing_outlet_rows);

The sample imports as a 13-by-4 matrix, and row 7 contains the missing outlet pressure. The shape check catches added or removed columns before the script assigns engineering meanings to their positions:

elapsed_s = data(:, 1);
inlet_pressure_kpa = data(:, 2);
outlet_pressure_kpa = data(:, 3);
flow_l_min = data(:, 4);

A dimension check cannot detect four columns that have been reordered. The named-column alternative later in this guide checks the header when column order is not controlled.

Keep each test record aligned

Time, pressure, and flow belong to the same recorded instant. One logical mask removes an incomplete row from every vector together:

valid = ~isnan(inlet_pressure_kpa) & ...
    ~isnan(outlet_pressure_kpa) & ...
    ~isnan(flow_l_min);

if sum(valid) == 0
    error("No complete measurement rows were found");
end

valid_elapsed_s = elapsed_s(valid);
valid_inlet_kpa = inlet_pressure_kpa(valid);
valid_outlet_kpa = outlet_pressure_kpa(valid);
valid_flow_l_min = flow_l_min(valid);
pressure_rise_kpa = valid_outlet_kpa - valid_inlet_kpa;

The sample retains 12 of 13 rows. Its mean pressure rise is 46.38 kPa, its peak pressure rise is 49.60 kPa, and its mean flow is 19.42 L/min. Those values come from the synthetic records in the downloadable CSV.

Inspect the missing reading and calculated result

The raw outlet-pressure trace breaks where the logger supplied NA. Plotting pressure rise against the filtered timestamps shows only measurements backed by complete records.

RunMat Desktop running MATLAB-syntax code to read a pump-test CSV and plot the missing measurement and validated pressure rise

figure(1);
plot(elapsed_s, outlet_pressure_kpa, "o-");
xlabel("Elapsed time (s)");
ylabel("Outlet pressure (kPa)");
title("Raw export: one reading is missing");
grid on;

figure(2);
plot(valid_elapsed_s, pressure_rise_kpa, "o-");
xlabel("Elapsed time (s)");
ylabel("Pressure rise (kPa)");
title("Validated rows: pressure rise");
grid on;

The gap remains visible in the raw data, while the derived result contains no fabricated zero or misaligned timestamp.

Write a CSV another system can verify

The receiving system needs column names and a stable five-column shape. Write the header once with writecell, then append the validated numeric rows with writematrix:

processed = [valid_elapsed_s, valid_inlet_kpa, valid_outlet_kpa, ...
    valid_flow_l_min, pressure_rise_kpa];

header = {"elapsed_s", "inlet_pressure_kpa", "outlet_pressure_kpa", ...
    "flow_l_min", "pressure_rise_kpa"};
writecell(header, "pump_test_validated.csv");
writematrix(processed, "pump_test_validated.csv", "WriteMode", "append");

round_trip = readmatrix("pump_test_validated.csv", "NumHeaderLines", 1);
assert(isequal(size(round_trip), [12 5]), ...
    "Validated output must contain 12 rows and 5 columns");
assert(max(abs(round_trip(:, 5) - pressure_rise_kpa)) < 1e-10, ...
    "Exported pressure rise does not match the calculated values");

The reread catches a missing header line, an incomplete append, or an unexpected output shape before another team or automated process consumes the file.

When column names define the contract

Some loggers preserve four columns but reorder them after a firmware or export-preset change. A table retains the header, allowing the script to reject renamed or reordered variables before calculation.

test = readtable("pump_test.csv", "TreatAsMissing", "NA");

required = ["elapsed_s", "inlet_pressure_kpa", ...
    "outlet_pressure_kpa", "flow_l_min"];

if ~isequal(string(test.Properties.VariableNames), required)
    error("pump_test.csv has unexpected or reordered columns");
end

test = rmmissing(test);
test.pressure_rise_kpa = test.outlet_pressure_kpa - ...
    test.inlet_pressure_kpa;
writetable(test, "pump_test_validated.csv");

fprintf("Validated table rows: %d\n", height(test));

rmmissing removes a table row when any variable is missing. writetable carries the verified variable names into the output. Keep the matrix workflow for controlled numeric exports; choose the table workflow when headers, dates, IDs, or mixed types belong to the file contract.

Run the established analysis on Linux

Once the file checks and calculations are fixed in one script, the same entrypoint can run on a Linux processing machine through the RunMat CLI:

runmat pump_test_analysis.m

Keep pump_test.csv beside the script or pass an explicit path in your own version. The CLI reference covers headless execution and runtime options. For recorded data that continues into frequency analysis, the MATLAB FFT guide follows a sensor CSV through spectral checks.

Other CSV layouts

Vendor exports may use different delimiters, regional number formats, append-only records, or custom text layouts. Start with the function that matches the file contract, then validate the assumptions your calculation depends on.

What is differentBetter starting pointWhat to check
Headers, IDs, dates, or mixed values matterreadtable, then writetableRequired variable names, inferred types, and missing rows
Fields use tabs, semicolons, or regional formatsreadmatrix with delimiter and separator optionsDelimiter, decimal separator, and thousands separator
New rows must be added to an existing filewritematrix or writecell with "WriteMode", "append"Existing column order and whether the header already exists
Records have a custom text layoutfopen with textscan or fprintfFailed opens, encoding, format strings, and fclose
An older script uses csvread or csvwritereadmatrix or writematrixZero-based offsets, selected ranges, and empty-field behavior

Frequently asked questions

How do I read a CSV file with headers in MATLAB?

Choose readtable when the column names matter. Choose readmatrix with "NumHeaderLines", 1 when the header can be skipped and the remaining fields are numeric.

Should I use readmatrix or readtable?

readmatrix returns a numeric array that is convenient for matrix calculations. readtable preserves names and mixed types, allowing the script to validate the header before using the columns.

How do I write a CSV file in MATLAB?

Use writematrix for a numeric array, writetable when variable names should become headers, or writecell for manually assembled mixed output.

How do I append rows to a CSV file?

Pass "WriteMode", "append" to writematrix or writecell. Confirm that the header already exists and every appended batch uses the same column order.

What causes an invalid file identifier?

fopen returns -1 when it cannot open a file. Check the identifier and returned error message before passing it to textscan, fprintf, fread, or fwrite, and close successful handles with fclose.