Most appropriate sub-area of p5.js?
p5.js version
2.3.2 (verified on main @ 94fb07d)
Web browser and version
Not browser-specific — the defect is in src/io/csv.js. Reproduced by importing the module directly in Node v25.9.0.
Operating system
All
Steps to reproduce this
Steps:
- Save a single-column CSV with no trailing newline, e.g. the three bytes
a\nb\nc written as name, Ada, Grace on three lines with no final \n.
- Load it with
loadTable().
- The last row is missing, with no error or warning.
Snippet:
// Reproduces directly against the module, no browser needed:
import { parse } from './src/io/csv.js';
parse('a\nb\nc'); // [["a"], ["b"]] <- "c" is gone
parse('a,b\nc,d'); // [["a","b"],["c","d"]] <- multi-column is unaffected
Via the public API:
let table;
async function setup() {
createCanvas(100, 100);
// data.csv contains: name\nAda\nGrace (no trailing newline)
table = await loadTable('data.csv', ',', 'header');
print(table.getRowCount()); // 1, expected 2
}
Cause
The tokenizer flushes the pending value at end of input only when ctx.entry is non-empty:
// src/io/csv.js:137
// flush the last value
if (ctx.entry.length !== 0) {
valueEnd(ctx);
entryEnd(ctx);
}
For a single-column file, entryEnd() resets ctx.entry to [] at every newline. So when the file does not end with a newline, the final value is still sitting in ctx.value while ctx.entry is empty, and the flush is skipped entirely. The row is discarded silently.
Multi-column files are unaffected, because at least one value has already been pushed into ctx.entry by the separator before the loop ends. That is why this has gone unnoticed — the common case works.
Expected behaviour
parse('a\nb\nc') should return [["a"], ["b"], ["c"]], and loadTable() should return every row in the file regardless of whether it ends with a newline. Trailing newlines are optional in RFC 4180, and plenty of tools and hand-edited files omit them.
Suggested fix
// flush the last value
- if (ctx.entry.length !== 0) {
+ if (ctx.entry.length !== 0 || ctx.value !== '') {
valueEnd(ctx);
entryEnd(ctx);
}
A fixture in test/unit/io/loadTable.js with a single-column file and no trailing newline would lock this in.
I'd be glad to open a PR with the fix and that test if this looks right to you.
Most appropriate sub-area of p5.js?
p5.js version
2.3.2 (verified on
main@ 94fb07d)Web browser and version
Not browser-specific — the defect is in
src/io/csv.js. Reproduced by importing the module directly in Node v25.9.0.Operating system
All
Steps to reproduce this
Steps:
a\nb\ncwritten asname,Ada,Graceon three lines with no final\n.loadTable().Snippet:
Via the public API:
Cause
The tokenizer flushes the pending value at end of input only when
ctx.entryis non-empty:For a single-column file,
entryEnd()resetsctx.entryto[]at every newline. So when the file does not end with a newline, the final value is still sitting inctx.valuewhilectx.entryis empty, and the flush is skipped entirely. The row is discarded silently.Multi-column files are unaffected, because at least one value has already been pushed into
ctx.entryby the separator before the loop ends. That is why this has gone unnoticed — the common case works.Expected behaviour
parse('a\nb\nc')should return[["a"], ["b"], ["c"]], andloadTable()should return every row in the file regardless of whether it ends with a newline. Trailing newlines are optional in RFC 4180, and plenty of tools and hand-edited files omit them.Suggested fix
A fixture in
test/unit/io/loadTable.jswith a single-column file and no trailing newline would lock this in.I'd be glad to open a PR with the fix and that test if this looks right to you.