示例
nimble_csv 1.3.0: Demonstrate selective field quoting and iodata streaming in NimbleCSV.dump_to_iodata and dump_to_stream, custom :reserved character quoting in define/2, and multiline row state preservation in parse_enumerable/2.
已验证示例 — hex nimble_csv 1.3.0: Demonstrate selective field quoting and iodata streaming in NimbleCSV.dump_to_iodata and dump_to_stream, custom :reserved…
sha256:4ca102627cb1041b56988dec8ec1d9fa2e7b495b667f2cffe6fda75b1bd121d6
本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。
提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。
MIT-0
执行证据
声明的环境与签名的运行分开呈现,你可以看到这个样本究竟运行了什么、在哪里运行。
- 证据依据
- 签名契约通过
- 验证回执
- 2
- 构建过它的签名密钥
- 2
声明的环境
elixir linux x64 elixir elixir mix
验证运行环境
| 环境 | 契约 | 阶段 | 运行日期 |
|---|---|---|---|
| elixir 1 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:PASS · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · hex@1 |
2026-08-17 |
| elixir 1 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:PASS · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · hex@1 |
2026-08-18 |
案例
HOW- 目标
- Demonstrate selective field quoting and iodata streaming in NimbleCSV.dump_to_iodata and dump_to_stream, custom :reserved character quoting in define/2, and multiline row state preservation in parse_enumerable/2.
- 符号
-
- NimbleCSV.RFC4180.dump_to_iodata
- NimbleCSV.RFC4180.dump_to_stream
- NimbleCSV.define
- NimbleCSV.RFC4180.parse_enumerable
- 环境
- elixir
- 创建时间
- 2026-08-17T18:52:52Z
契约
- NimbleCSV.RFC4180.dump_to_iodata selectively quotes and doubles escape characters only for fields containing reserved delimiter, escape, or newline characters, while leaving empty strings and unreserved fields bare.
- assert dump_to_stream lazily emits each row as iodata with CRLF row terminators
- assert custom parser defined with :reserved forces quoting for configured characters such as spaces and hashes
- assert parse_enumerable preserves multiline quoted fields across enumerable elements and respects skip_headers
- assert streaming rows through dump_to_stream and converting iodata chunks to binaries roundtrips losslessly through parse_enumerable
文件
- NOTES.md
- csx.json
- mix.exs
- mix.lock
- test/contract.exs
源代码
# NimbleCSV 1.3.0 Dumping, Custom Reserved Delimiters, and Enumerable Parsing
A search via known solutions returned existing samples for nimble_csv:
- `sha256:d3b935a442e69a84c6664c75e2a07c5c6354c86243d8f2f795aa07d17eb5619c` (`to_line_stream/1` chunk reassembly and OWASP `escape_formula` prefixes)
- `sha256:ae21b951308d6bac196db85fb726efc75244ff77210adc6af5babba86658e6c1` (`parse_string/2` default `skip_headers: true`, quote unescaping, and UTF-8 BOM preservation)
- `sha256:96745beca2a7ac60bfcc923ac4a93c445eb71335e7326488531561ca031c2f6c` (multi-separator list where the dumper fixes separator to the first element)
- `sha256:c5a8d3d8e7491c41eec46559763ce8367d36a8dbcbeab7e5bc8d95670b4d2d09` (error paths on trailing tokens after quotes and non-binary inputs)
This sample tests the exact behavior of dumping mechanisms, custom reserved character escaping, and enumerable line aggregation in pinned release `1.3.0`.
## Observed Behavior
1. **Selective Quoting in Dumper**: `NimbleCSV.RFC4180.dump_to_iodata/1` inspects each field against configured `:reserved` characters (by default: comma `,`, double-quote `"`, `\r\n`, and `\n`).
- Fields containing no reserved characters (e.g. plain text, integers) and empty strings `""` are emitted unquoted (e.g. `["a", "", "b"]` renders as `"a,,b\r\n"`).
- Fields containing reserved characters are enclosed in double quotes `"` and internal double quotes are escaped by doubling (`""`).
- An empty row `[]` emits a standalone line separator (`"\r\n"`).
2. **Lazy Iodata Streaming**: `NimbleCSV.RFC4180.dump_to_stream/1` returns an Elixir `Stream` yielding each row as `iodata()` terminated with `\r\n`.
3. **Custom `:reserved` Characters in `define/2`**: Providing an explicit `:reserved` list in `NimbleCSV.define/2` specifies the exact set of substrings that trigger quoting during dump operations. For example, adding `" "` and `"#"` causes fields containing spaces or hash marks to be quoted in the dumped output.
4. **Multiline State Across Enumerable Items**: `NimbleCSV.RFC4180.parse_enumerable/2` iterates through an enumerable of pre-segmented lines. When an open quoted field spans multiple elements in the enumerable, the parser preserves state across chunks and reconstructs the multiline field into a single cell.
5. **Streaming Roundtrip**: Converting `dump_to_stream/1` iodata chunks to binary strings via `IO.iodata_to_binary/1` and feeding them into `parse_enumerable(skip_headers: false)` produces an exact match with the original input data.
{"buildCommand":["mix","deps.compile","--no-deps-check"],"case":{"caseId":"case:sha256:1d093210329d8c0755dda45267114d42741caf9130453359d7289d2b1a4adefb","contract":["NimbleCSV.RFC4180.dump_to_iodata selectively quotes and doubles escape characters only for fields containing reserved delimiter, escape, or newline characters, while leaving empty strings and unreserved fields bare.","assert dump_to_stream lazily emits each row as iodata with CRLF row terminators","assert custom parser defined with :reserved forces quoting for configured characters such as spaces and hashes","assert parse_enumerable preserves multiline quoted fields across enumerable elements and respects skip_headers","assert streaming rows through dump_to_stream and converting iodata chunks to binaries roundtrips losslessly through parse_enumerable"],"goal":"Demonstrate selective field quoting and iodata streaming in NimbleCSV.dump_to_iodata and dump_to_stream, custom :reserved character quoting in define/2, and multiline row state preservation in parse_enumerable/2.","kind":"HOW","packages":["pkg:hex/nimble_csv@1.3.0"],"schemaVersion":1,"symbols":["NimbleCSV.RFC4180.dump_to_iodata","NimbleCSV.RFC4180.dump_to_stream","NimbleCSV.define","NimbleCSV.RFC4180.parse_enumerable"]},"contractCommand":["mix","run","test/contract.exs","--no-deps-check"],"environment":{"arch":"x64","ecosystem":"hex","executionContext":"elixir","language":"elixir","os":"linux","packageManager":"mix","runtime":"elixir","schemaVersion":1},"license":"MIT-0","packages":["pkg:hex/nimble_csv@1.3.0"],"schemaVersion":1,"symbols":["NimbleCSV.RFC4180.dump_to_iodata","NimbleCSV.RFC4180.dump_to_stream","NimbleCSV.define","NimbleCSV.RFC4180.parse_enumerable"],"verifierAdapter":"hex@1"}
defmodule NimbleCsvVersionDepth.MixProject do
use Mix.Project
def project do
[
app: :nimble_csv_version_depth,
version: "0.1.0",
elixir: "~> 1.15",
deps: deps()
]
end
def application, do: [extra_applications: []]
defp deps do
[{:nimble_csv, "1.3.0"}]
end
end
%{
"nimble_csv": {:hex, :nimble_csv, "1.3.0", "b7f998dc62b222bce9596e46f028c7a5af04cb5dde6df2ea197c583227c54971", [:mix], [], "hexpm", "41ccdc18f7c8f8bb06e84164fc51635321e80d5a3b450761c4997d620925d619"},
}
# Prove NimbleCSV 1.3.0 dumping rules, streaming mechanics, custom reserved delimiters, and parse_enumerable behavior
alias NimbleCSV.RFC4180, as: CSV
# ---------------------------------------------------------------------------
# 1. Selective quoting rules in dump_to_iodata/1
# ---------------------------------------------------------------------------
# NimbleCSV quotes and doubles escape characters only when a field contains
# reserved characters (separator, escape, or newlines). Empty strings and
# unreserved fields remain unquoted.
rows = [
["alpha", "", "123", "has,comma", "has\"quote", "has\r\nnewline"],
["second", "plain", "row", "end"]
]
dumped_iodata = CSV.dump_to_iodata(rows) |> IO.iodata_to_binary()
expected_dump =
"alpha,,123,\"has,comma\",\"has\"\"quote\",\"has\r\nnewline\"\r\n" <>
"second,plain,row,end\r\n"
unless dumped_iodata == expected_dump do
raise "dump_to_iodata mismatch:\nExpected: #{inspect(expected_dump)}\nGot: #{inspect(dumped_iodata)}"
end
# An empty row [] produces a standalone line separator
empty_row_dump = CSV.dump_to_iodata([[]]) |> IO.iodata_to_binary()
unless empty_row_dump == "\r\n" do
raise "empty row dump mismatch: got #{inspect(empty_row_dump)}"
end
# ---------------------------------------------------------------------------
# 2. Lazy row streaming with dump_to_stream/1
# ---------------------------------------------------------------------------
# dump_to_stream returns a lazy Stream emitting iodata for each row
stream_chunks =
rows
|> CSV.dump_to_stream()
|> Enum.map(&IO.iodata_to_binary/1)
expected_chunks = [
"alpha,,123,\"has,comma\",\"has\"\"quote\",\"has\r\nnewline\"\r\n",
"second,plain,row,end\r\n"
]
unless stream_chunks == expected_chunks do
raise "dump_to_stream mismatch:\nExpected: #{inspect(expected_chunks)}\nGot: #{inspect(stream_chunks)}"
end
# ---------------------------------------------------------------------------
# 3. Custom :reserved characters and :line_separator in NimbleCSV.define/2
# ---------------------------------------------------------------------------
# Custom parsers can specify additional reserved characters to force quoting
NimbleCSV.define(CustomReservedParser,
separator: ";",
escape: "\"",
line_separator: "\n",
reserved: [";", "\"", "\n", " ", "#"]
)
custom_input = [
["item1", "with space", "tag#9", "plain"]
]
custom_dumped = CustomReservedParser.dump_to_iodata(custom_input) |> IO.iodata_to_binary()
expected_custom_dump = "item1;\"with space\";\"tag#9\";plain\n"
unless custom_dumped == expected_custom_dump do
raise "custom dump mismatch:\nExpected: #{inspect(expected_custom_dump)}\nGot: #{inspect(custom_dumped)}"
end
# ---------------------------------------------------------------------------
# 4. parse_enumerable/2 multiline state preservation across enumerable elements
# ---------------------------------------------------------------------------
# When quoted fields span multiple enumerable elements, parse_enumerable
# maintains parser state across elements to reconstruct the complete row.
enumerable_input = [
"header1,header2,header3\r\n",
"data1,\"line one\r\n",
"line two\r\n",
"line three\",data3\r\n",
"next1,next2,next3\r\n"
]
# Default skip_headers: true skips the first line
parsed_default = CSV.parse_enumerable(enumerable_input)
expected_parsed_default = [
["data1", "line one\r\nline two\r\nline three", "data3"],
["next1", "next2", "next3"]
]
unless parsed_default == expected_parsed_default do
raise "parse_enumerable default mismatch:\nExpected: #{inspect(expected_parsed_default)}\nGot: #{inspect(parsed_default)}"
end
# Explicit skip_headers: false includes all rows
parsed_all = CSV.parse_enumerable(enumerable_input, skip_headers: false)
expected_parsed_all = [
["header1", "header2", "header3"],
["data1", "line one\r\nline two\r\nline three", "data3"],
["next1", "next2", "next3"]
]
unless parsed_all == expected_parsed_all do
raise "parse_enumerable (skip_headers: false) mismatch:\nExpected: #{inspect(expected_parsed_all)}\nGot: #{inspect(parsed_all)}"
end
# ---------------------------------------------------------------------------
# 5. Full roundtrip: dump_to_stream piped to parse_enumerable
# ---------------------------------------------------------------------------
# Streaming rows through dump_to_stream, converting iodata chunks to binaries,
# and parsing via parse_enumerable preserves all special characters and formatting.
complex_dataset = [
["sku", "name", "description", "details"],
["A100", "Widget", "Line 1\r\nLine 2\r\nLine 3", "Special \"quoted\" value"],
["B200", "Gadget, Pro", "", "Unicode: \u00E9\u00E8\u00E0"],
["C300", "Basic", "No special chars", ""]
]
roundtrip_result =
complex_dataset
|> CSV.dump_to_stream()
|> Stream.map(&IO.iodata_to_binary/1)
|> CSV.parse_enumerable(skip_headers: false)
unless roundtrip_result == complex_dataset do
raise "roundtrip result mismatch:\nExpected: #{inspect(complex_dataset)}\nGot: #{inspect(roundtrip_result)}"
end
IO.puts("contract passed")