Skip to content

Writing CSV data - #1

Merged
lpheller merged 6 commits into
mainfrom
feat/write-csv
Aug 17, 2026
Merged

Writing CSV data#1
lpheller merged 6 commits into
mainfrom
feat/write-csv

Conversation

@lpheller

@lpheller lpheller commented Jul 18, 2024

Copy link
Copy Markdown
Owner

Adds a writer.

Csv::make($rows)->toFile('data.csv')->write();      // replaces the file
Csv::make($rows)->toFile('data.csv')->append();     // at the end
Csv::make($rows)->toFile('data.csv')->insertAt(3);  // in front of record 3

withHeaders() writes a header row and fixes the column order, so associative rows land in the right columns no matter what order their keys are in, and a column a row doesn't carry is written empty instead of shifting everything left. Objects go out by their public properties, so whatever mapToObject() returns can be written straight back.

delimiter(), escape() and crlf() cover the format, bom() puts a UTF-8 BOM in front. Excel on Windows wants all three:

Csv::make($rows)->delimiter(';')->crlf()->bom()->toFile('export.csv')->write();

insertAt() is the only one that touches an existing file, so it takes some care:

  • records are counted with fgetcsv, so a newline inside a quoted field doesn't shift the position
  • everything after the insert is copied with stream_copy_to_stream and stays byte for byte identical, no reformatting
  • the file is rebuilt beside itself and renamed into place, so a crash can't leave it half written, and permissions carry over
  • memory is constant, inserting into a 1M row file takes about 0.3s

Positions count records from 1 and the header is record 1, so the first data row is position 2.

Also in here, on the read side

Csv::read()->encoding('Windows-1252') converts while reading, as a stream filter, so it costs about 10% on a million rows. Without it non-ASCII comes back as invalid UTF-8 and toJson() throws on it.

Behaviour change: fields are now parsed per RFC 4180, where a backslash is just a character. Previously a field ending in one swallowed its closing quote and ate the rest of the line. escape('\\') brings the old behaviour back for files that need it, such as a MySQL SELECT ... INTO OUTFILE dump. Worth a minor bump.

Housekeeping

This force-pushes over the old WIP branch, which predates the changes to FileHandler and CsvProcessor on main. The previous head was 32f901f if anything from it is still wanted.

@lpheller
lpheller marked this pull request as draft July 18, 2024 10:06
@lpheller lpheller changed the title WIP: Writing CSV data Writing CSV data Aug 17, 2026
@lpheller
lpheller marked this pull request as ready for review August 17, 2026 07:05
@lpheller
lpheller merged commit da3d8f1 into main Aug 17, 2026
47 of 49 checks passed
@lpheller
lpheller deleted the feat/write-csv branch August 17, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant