commit 3253d8c94f2c34d1155b4699cebf2c05625d7db5 Author: XSQRL Date: Tue Mar 19 12:46:28 2024 +0100 Initial state diff --git a/beitragsquittung.rb b/beitragsquittung.rb new file mode 100755 index 0000000..37ab606 --- /dev/null +++ b/beitragsquittung.rb @@ -0,0 +1,137 @@ +#!/usr/bin/env ruby +require 'cmxl' # for parsing the MT940 file +require 'roo' # for parsing the Excel file +require 'humanize' # for converting numbers to words + +Humanize.configure do |config| + config.default_locale = :de # [:en, :es, :fr, :tr, :de, :id], default: :en + config.decimals_as = :number # [:digits, :number], default: :digits +end + +# usage ./beitragsquittung.rb +# the member ID is optional. If not provided, all members are processed +# the time range is used from the MT940 file + +def generate_reciept(mitgliedsnummer, name, vorname, strasse, hausnummer, plz, ort, betrag, betrag_in_worten, start_datum, end_datum, payments) + puts "Generating reciept for #{mitgliedsnummer} #{name} #{vorname} #{strasse} #{hausnummer} #{plz} #{ort} #{betrag} #{betrag_in_worten} #{start_datum} #{end_datum}" + + # create a csv file with the payments + CSV.open("payments.csv", "wb") do |csv| + csv << ["datum", "betrag", "type", "verzicht"] + payments.each do |payment| + csv << [payment[:date], payment[:amount], payment[:type], payment[:verzicht]] + end + end + + # call latex to generate the pdf + system("pdflatex \"\\newcommand{\\vorname}{#{vorname}} \\newcommand{\\nachname}{#{name}} \\newcommand{\\strasse}{#{strasse + " " + hausnummer.to_s}} \\newcommand{\\plz}{#{plz}} \\newcommand{\\ort}{#{ort}} \\newcommand{\\betrag}{#{betrag}} \\newcommand{\\betraginworten}{#{betrag_in_worten}} \\newcommand{\\startdatum}{#{start_datum}} \\newcommand{\\findatum}{#{end_datum}} \\newcommand{\\mitgliedsnummer}{#{mitgliedsnummer}} \\input{document.tex}\"") +end + +def get_payments_from_member_id(mitgliedsnummer) + + Cmxl.config[:statement_separator] = /\n-.\n/m + Cmxl.config[:raise_line_format_errors] = true + + # get the filename from the command line arguments + filename = ARGV[0] + + # check if the filename is valid or not presented + if filename.nil? || !File.exists?(filename) + puts "Enter the filename of the MT940 statement to parse" + filename = gets.chomp + end + + # parse the MT940 file + statements = Cmxl.parse(File.read(filename), :encoding => 'ISO-8859-1') + + # The statement contains transactions called Mitgliedsbeitrag. They are labled like this: "MREF"=>"Mitgliedsbeitrag Nr. 17" where 17 is the mitgliedsnummer. + # for each payment of the mitgliedsbeitrag create an entry with the date and amount in the following datastructure + payments = [] + + statements.each do |s| + s.transactions.each do |t| + reference = "" + t.sepa.each do |sf| + # add the second entry of the sepa reference (sf) to the reference string if the first entry contans "MREF" + if sf[0] == "MREF" + reference = sf[1] + end + + end + + # if the mitgliedsnummer is a single digit number, the reference is "Mitgliedsbeitrag Nr. 01" and not "Mitgliedsbeitrag Nr. 1" + # so we have to check for both cases + if reference.include?("Mitgliedsbeitrag Nr. #{mitgliedsnummer.to_s.rjust(2, '0')}") || reference.include?("Mitgliedsbeitrag Nr. #{mitgliedsnummer}") + # puts t.entry_date + # puts t.amount + payment = { + date: t.entry_date, + amount: t.amount, + type: "Mitgliedsbeitrag", + verzicht: "nein" + } + payments << payment + # puts + end + end + end + + # get the start and end date of the statements + start_datum = statements[0].opening_balance.date.strftime("%d.%m.%Y") + end_datum = statements[statements.length() -1].closing_balance.date.strftime("%d.%m.%Y") + + return payments, start_datum, end_datum +end + +def get_member_details_from_mitgliedsnummer(mitgliedsnummer) + # get the member details from the excel file + # the member details are stored in the first sheet + # the first row contains the headings of the columns + # the second row contains the values for the first member + # the headings are: Nr., Nachname, Vorname, Geburtsdatum, Straße, Hausnummer, Postleitzahl, Ort + # the values are: 1, Mustermann, Max, 01.01.1970, Musterstraße, 1, 12345, Musterstadt + xlsx = Roo::Spreadsheet.open(ARGV[1]) + + # create data structure + member = { + name: xlsx.sheet(0).row(mitgliedsnummer+1)[1], + vorname: xlsx.sheet(0).row(mitgliedsnummer+1)[2], + strasse: xlsx.sheet(0).row(mitgliedsnummer+1)[4], + hausnummer: xlsx.sheet(0).row(mitgliedsnummer+1)[5], + plz: xlsx.sheet(0).row(mitgliedsnummer+1)[6], + ort: xlsx.sheet(0).row(mitgliedsnummer+1)[7] + } + return member +end + +mitgliedsnummer = ARGV[2].to_i +if mitgliedsnummer.nil? + # get number of members from the excel file it is the first value of the last row + xlsx = Roo::Spreadsheet.open(ARGV[1]) + anzahl_members = xlsx.sheet(0).last_row[0] + puts "Number of members: #{anzahl_members}" + + # loop over all members + for mitgliedsnummer in 1..anzahl_members + puts "Processing member #{mitgliedsnummer}" + get_payments_from_member_id(mitgliedsnummer) + generate_reciept(mitgliedsnummer, name, vorname, strasse, plz, ort, betrag, betrag_in_worten, start_datum, end_datum) + end +else + # get the payments from the MT940 file + payments, start_datum, end_datum = get_payments_from_member_id(mitgliedsnummer) + puts "Payments:" + total_payment = 0 + payments.each do |payment| + puts payment[:date] + " " + payment[:amount].to_s + total_payment += payment[:amount] + end + + # convert the total payment to a string in german + betrag_in_worten = total_payment.humanize + puts "Total payment:" + total_payment.to_s + " in worten: " + betrag_in_worten + puts + + member = get_member_details_from_mitgliedsnummer(mitgliedsnummer) + generate_reciept(mitgliedsnummer, member[:name], member[:vorname], member[:strasse], member[:hausnummer], member[:plz], member[:ort], total_payment, betrag_in_worten, start_datum, end_datum, payments) +end diff --git a/csvsimple/CHANGES.md b/csvsimple/CHANGES.md new file mode 100644 index 0000000..18e055d --- /dev/null +++ b/csvsimple/CHANGES.md @@ -0,0 +1,421 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on +[Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to +[Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security + + +## [2.5.0] - 2023-10-16 + +### Added +- New survey tables (issue #29): + - `\csvautotabularray` + - `\csvautotabularray*` + - `\csvautolongtabularray` + - `\csvautolongtabularray*` +- New corresponding options (issue #29) + - `autotabularray` + - `autotabularray*` + - `autolongtabularray` + - `autolongtabularray*` + +### Changed +- Documentation typos fixed (issue #30) +- Survey tables moved into a separate section + +### Fixed +- `collect data` does not collect `after first line` (issue #28) + + + +## [2.4.0] - 2023-05-09 + +### Added +- New additive filter options (issue #24): + - `and filter bool` + - `and filter fp` + - `and filter strcmp` + - `and filter not strcmp` + - `or filter bool` + - `or filter fp` + - `or filter strcmp` + - `or filter not strcmp` +- Hook `csvsimple/csvline` added with example for doublequote replacement (issue #19) +- Option setting `separator = space` (issue #20) + +### Changed +- The content of `range` is now expanded before processing it +- Implementation of `filter bool`, `filter fp`, `filter strcmp`, `filter not strcmp` + + + +## [2.3.2] - 2022-09-20 + +### Fixed +- The `\ifcsvfirstrow` command doesn't work (issue #23) + + + +## [2.3.1] - 2022-06-21 + +### Fixed +- Documentation: placeholder names corrected (issue #21) +- Use built-in constants instead of integers for boolexpr (issue #22) + + + +## [2.3.0] - 2022-02-01 + +### Added +- Option `column names detection` (related to issue #16) +- Documentation: csvautotabular inspired example for tabularray. + +### Changed +- `\csvlinetotablerow` changed to be expandable and to be + usable inside a *Data Collection* (issue #16) +- Options `autotabular`, `autobooktabular`, `autolongtable`, `autobooklongtable` + use `column names detection=false` now. + + + +## [2.2.1] - 2022-01-07 + +### Fixed +- Option `late after first line` ignored for `csvsimple-l3` (issue #17) + + + +## [2.2.0] - 2021-09-09 + +### Added +- Configurable usage of tabular-like environments (issue #12) +- Option `table centered` +- Option `generic table` +- Option `generic collected table` +- Option `generic table options` + +### Changed +- The implementation of all tabular-like environments uses the new + generic environments now internally. Should be unnoticeable for a user. + +### Fixed +- documentation typo correction and small improvents (issue #15) +- Option `long tabularray` + + + +## [2.1.0] - 2021-07-06 + +### Added +- Support for (limited) data collection (issue #15) +- Support for package `tabularray` (part of issue #12) +- Option `after filter` +- Option `collect data` +- Option `data collection` +- Option `tabularray` +- Option `centered tabularray` +- `\csvdatacollection` +- `\csvexpval` +- `\csvexpnot` +- `\csvcollectn` +- `\csvcollectx` +- `\csvcollectV` + +### Fixed +- Line Range was not resetted +- Wrong link and word inside documentation `csvsimple.pdf` (issue #13, issue #15) +- Several inconsistent local/global assignment errors + + + +## [2.0.0] - 2021-06-29 + +### Added +- New documentation `csvsimple-l3.pdf` for the new LaTeX3 version + (revised, adapted and extended from the old documentation) +- `\thecsvcolumncount` +- Option `autotabular*` +- Option `autobooktabular*` +- Option `autolongtable*` +- Option `autobooklongtable*` +- Option `filter bool` +- Option `filter fp` +- Option `range` +- `\csvautotabular*` +- `\csvautobooktabular*` +- `\csvautolongtable*` +- `\csvautobooklongtable*` +- `\csvfilterbool` +- `\ifcsvfirstrow` +- `\ifcsvoddrow` +- `\ifcsvfpcmp` +- `\ifcsvintcmp` +- `\csvsortingrule` + +### Changed +- Complete re-implementation of the hitherto existing latex package + as LaTeX3 package using the expl3 interface. From now on, three package + files are provided: + ** `csvsimple-legacy.sty` identical to csvsimple until version 1.22 ** + ** `csvsimple-l3.sty` LaTeX3 package of csvsimple ** + ** `csvsimple.sty` stub to select `l3` or `legacy` (default) ** +- The LaTeX2e version (`csvsimple-legacy`) will be maintained in its + current state with no intended changes with exceptions of bug fixes. +- The LaTeX3 version (`csvsimple-l3`) is regarded to be the main package + and may receive feature upgrades in the future +- Existing documents using csvsimple v1.22 need no change since loading + `csvsimple` will load `csvsimple-legacy`. +- `cvsimple-l3` is a *nearly* drop-in replacement for `csvsimple-legacy`. + Only very few things phased out and the user interface is quite identical. + The most significant difference is that `l3keys` are used instead of `pgfkeys` + which may need adaptions on user side (for examples, if .styles are used) +- New documents are encouraged to apply `cvsimple-l3` instead of `csvsimple-legacy`. +- For the complete package is valid: do not upgrade from version 1.22, if your + TeX installation has no current LateX3/expl3 support a.k.a *is too old* +- `csvinputline` and `csvrow` are no longer LaTeX2e counters +- The hitherto existing documentation `csvsimple.pdf` is now `csvsimple-legacy.pdf` +- `csvsimple.pdf` documents the stub package and differences + between `csvsimple-l3.sty` and `csvsimple-legacy.sty` +- `column count = 0` means automatic column number detection for CSV files without head +- Option `head` does not change option `check column count` anymore +- Changelog moved from CHANGES to CHANGES.md and adapted to + [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +- From now on version numbers adhere to + [Semantic Versioning](http://semver.org/spec/v2.0.0.html) + +### Deprecated +- `\csviffirstrow` +- `\csvifoddrow` + +### Removed +- `\csvheadset` +- Option `filter` +- Option `nofilter` +- Option `nohead` + + + +## [1.22] - 2021-06-07 + +### Added +- Option `head to column names prefix` (issue #7) + +### Changed +- Due to changes in the LaTeX kernel 2021-06-01, the empty line + detection of csvsimple had to be adapted. Updating csvsimple is + essential to avoid problems with kernel 2021-06-01. (issue #11) + + + +## [1.21] - 2019-04-09 + +### Changed +- Package `pgfrcs` added as required package +- Introduction augmented with additional hints for first time users (issue #3) + +### Fixed +- Spurious blank in sorting code removed + + + +## [1.20] - 2016-07-01 + +### Added +- New string comparison macros: +- `\ifcsvstrequal` +- `\ifcsvprostrequal` +- `\ifcsvstrcmp` +- `\ifcsvnotstrcmp` +- New filter options: +- Option `filter ifthen` +- Option `filter test` +- Option `filter expr` +- Option `full filter` +- Option `filter strcmp` +- Option `filter not strcmp` + +### Changed +- Implementation changed from `\roman` to `\romannumeral` +- `\write18` replaced by `\ShellEscape` from the shellesc package +- `\csvlinetotablerow` implemented more efficiently +- `\csvloop` made long +- Code optimizations +- Documentation revised + + + +## [1.12] - 2014-07-14 + +### Added +- Option `csvsorter token` +- Documentation extended with siunitx examples + +### Changed +- Success of CSV-Sorter call is checked (Note: Update to CSV-Sorter v0.94 or newer!) +- Encircling column entry braces removed for all entries for better siunitx compatibility +- Documentation revised + +### Fixed +- CSV-Sorter call incompatibilities with the ngerman package (not babel) + + + +## [1.11] - 2014-07-08 + +### Changed +- If a CSV file with an empty first line is found, csvsimple + stops with an error message + +### Fixed +- Sorting preprocessor overwrites the input data in some combinations + + + +## [1.10] - 2014-07-07 + +### Added +- `\csvautobooktabular` +- `\csvautobooklongtable` +- External sorting specifically supported for the CSV-Sorter tool with the new options +- Option `csvsorter command` +- Option `csvsorter configpath` +- Option `csvsorter log` +- Option `sort by` +- Option `new sorting rule` +- New keys for respecting special characters: +- Option `respect tab` +- Option `respect percent` +- Option `respect sharp` +- Option `respect dollar` +- Option `respect and` +- Option `respect backslash` +- Option `respect underscore` +- Option `respect tilde` +- Option `respect circumflex` +- Option `respect leftbrace` +- Option `respect rightbrace` +- Option `respect all` +- Option `respect none` +- Option setting `separator = tab` + +### Changed +- If a CSV file is not found, csvsimple stops with an error message instead of a warning + +### Fixed +- Table head names in curly brackets were not recognized for some cases + + + +## [1.07] - 2013-09-25 + +### Added +- Option `separator` to set the data value separator to + `comma`, `semicolon`, or `pipe` + +### Changed +- Internal macro `\TrimSpaces` renamed to avoid name clashed with `xparse` + + + +## [1.06] - 2012-11-08 + +### Changed +- Implementation for line breaking changed from full macro expansion to + token expansion. This allows quite arbitrary macro code inside the data. + Note that this may be a breaking change if your application expects + expanded column values. +- Option values added for `\csvautotabular` and `\csvautolongtable` + + + +## [1.05] - 2012-03-12 + +### Added +- Source code of the documentation added +- Provision of the csvsimple.tds.zip file for easier installation +- Option `preprocessed file` +- Option `preprocessor` +- Option `no preprocessing` + +### Changed +- Documentation language changed from German to English +- Option `nocheckcolumncount` renamed to `no check column count` +- Option `nofilter` renamed to `no check column count` +- Option `nocheckcolumncount` renamed to `no filter` +- Option `nohead` renamed to `no head` + +### Deprecated +- Option `nofilter` +- Option `nohead` + +### Removed +- Option `@table` removed from the documentation + +### Fixed +- Error in `nocheckcolumncount` corrected and key renamed to 'no check column count' + + + +## [1.04] - 2011-11-11 + +### Added +- Option `head to column names` (automatic column names) +- Option `no table` +- Column numbers can now be used for column macro definitions + +### Changed +- Internal behaviour of `before reading` and `after reading` + changed for tables + +### Fixed +- documentation update and correction + + + +## [1.03] - 2011-11-04 + +### Fixed +- Processing error for lines starting with '00' corrected + + + +## [1.02] - 2011-04-04 + +### Added +- `\csvfilteraccept` +- `\csvfilterreject` +- Option `filter accept all` +- Option `filter reject all` + +### Fixed +- Error in the documentation for longtable und tabbing corrected + + + +## [1.01] - 2010-11-10 + +### Added +- Option `after first line` +- Option `late after first line` +- New example for key evaluation in the documentation + +### Changed +- Documentation of some options clarified + + + +## [1.00] - 2010-07-28 + +### Added +- Initial public release diff --git a/csvsimple/README.md b/csvsimple/README.md new file mode 100644 index 0000000..d72e0a6 --- /dev/null +++ b/csvsimple/README.md @@ -0,0 +1,62 @@ +# The LaTeX package csvsimple - version 2.5.0 (2023/10/16) + + +> Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm + +> This work may be distributed and/or modified under the +> conditions of the LaTeX Project Public License, either version 1.3 +> of this license or (at your option) any later version. +> The latest version of this license is in +> http://www.latex-project.org/lppl.txt +> and version 1.3 or later is part of all distributions of LaTeX +> version 2005/12/01 or later. + +> This work has the LPPL maintenance status `author-maintained`. + +> This work consists of all files listed in README.md + + +`csvsimple` provides a simple *LaTeX* interface for the processing of files +with comma separated values (CSV). `csvsimple` relies heavily on a key value +syntax which results in an easy way of usage. Filtering and table generation +is especially supported. Since the package is considered as a lightweight +tool, there is no support for data sorting or data base storage. + + +## Contents of the package + +- `README.md` this file +- `CHANGES.md` log of changes (history) +- `csvsimple.sty` LaTeX package file (style file) +- `csvsimple-l3.sty` LaTeX package file (style file) +- `csvsimple-legacy.sty` LaTeX package file (style file) +- `csvsimple.pdf` Documentation for csvsimple +- `csvsimple-l3.pdf` Documentation for csvsimple (LaTeX3) +- `csvsimple-legacy.pdf` Documentation for csvsimple (Legacy) +- `csvsimple.tex` Source code of the documentation +- `csvsimple-l3.tex` Source code of the documentation +- `csvsimple-legacy.tex` Source code of the documentation +- `csvsimple-doc.sty` Source code of the documentation +- `csvsimple-title.png` Picture for the documentation +- `csvsimple-example.tex` Example file for package usage +- `csvsimple-example.csv` CSV file as part of the example +- `csvsimple-example.pdf` Compiled example +- `amountsort.xml` csvsorter configuration file (example) +- `catsort.xml` csvsorter configuration file (example) +- `encoding.xml` csvsorter configuration file (example) +- `gradesort.xml` csvsorter configuration file (example) +- `matriculationsort.xml` csvsorter configuration file (example) +- `namesort.xml` csvsorter configuration file (example) +- `transform.xml` csvsorter configuration file (example) + + +## Installation + +Copy the contents of the `csvsimple.tds.zip` from CTAN to your local TeX file tree. + +Alternatively, put the files to their respective locations within the TeX installation: + +- `csvsimple.sty` -> /tex/latex/csvsimple +- `csvsimple-l3.sty` -> /tex/latex/csvsimple +- `csvsimple-legacy.sty` -> /tex/latex/csvsimple +- all other files -> /doc/latex/csvsimple diff --git a/csvsimple/amountsort.xml b/csvsimple/amountsort.xml new file mode 100644 index 0000000..1f37624 --- /dev/null +++ b/csvsimple/amountsort.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/csvsimple/catsort.xml b/csvsimple/catsort.xml new file mode 100644 index 0000000..ac8e44a --- /dev/null +++ b/csvsimple/catsort.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/csvsimple/csvsimple-doc.sty b/csvsimple/csvsimple-doc.sty new file mode 100644 index 0000000..36f052a --- /dev/null +++ b/csvsimple/csvsimple-doc.sty @@ -0,0 +1,115 @@ +% !TeX encoding=UTF-8 +%% The LaTeX package csvsimple - version 2.5.0 (2023/10/16) +%% csvsimple-doc.sty: style file for the documentation +%% +%% ------------------------------------------------------------------------------------------- +%% Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm +%% ------------------------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `author-maintained'. +%% +%% This work consists of all files listed in README.md +%% +\def\version{2.5.0}% +\def\datum{2023/10/16}% + +\IfFileExists{csvsimple-doc.cfg}{\input{csvsimple-doc.cfg}}{}\providecommand\csvpkgprefix{} + +% prevent hypdoc from loading (sets hyperindex=false) +\@namedef{ver@hypdoc.sty}{9999/12/31} +\@namedef{opt@hypdoc.sty}{hypdoc} + + +\RequirePackage[T1]{fontenc} +\RequirePackage[utf8]{inputenc} +\RequirePackage[english]{babel} +\RequirePackage{lmodern,parskip,array,ifthen,calc,makeidx} +\RequirePackage{amsmath,amssymb} +\RequirePackage[svgnames,table,hyperref]{xcolor} +\RequirePackage{tikz,siunitx,xfp,tabularray} +\RequirePackage{varioref} +\RequirePackage[pdftex,bookmarks,raiselinks,pageanchor,hyperindex,colorlinks]{hyperref} +\urlstyle{sf} +\RequirePackage{cleveref} + +\RequirePackage[a4paper,left=2.5cm,right=2.5cm,top=1.5cm,bottom=1.5cm, + marginparsep=3mm,marginparwidth=18mm, + headheight=0mm,headsep=0cm, + footskip=1.5cm,includeheadfoot]{geometry} +\RequirePackage{fancyhdr} +\fancyhf{} +\fancyfoot[C]{\thepage}% +\renewcommand{\headrulewidth}{0pt} +\renewcommand{\footrulewidth}{0pt} +\pagestyle{fancy} +\tolerance=2000% +\setlength{\emergencystretch}{20pt}% + +\RequirePackage{longtable,booktabs,ifthen,etoolbox} + +\RequirePackage{tcolorbox} +\tcbuselibrary{skins,xparse,minted,breakable,documentation,raster} + +\definecolor{Green_Dark}{rgb}{0.078431,0.407843,0.176471} +\definecolor{Blue_Dark}{rgb}{0.090196,0.211765,0.364706} +\definecolor{Blue_Bright}{rgb}{0.858824,0.898039,0.945098} + +\tcbset{skin=enhanced, + minted options={fontsize=\footnotesize}, + doc head={colback=yellow!10!white,interior style=fill}, + doc head key={colback=magenta!5!white,interior style=fill}, + color key=DarkViolet, + color value=Teal, + color color=Teal, + color counter=Orange!85!black, + color length=Orange!85!black, + index colorize, + index annotate, + beforeafter example/.style={ + before skip=4pt plus 2pt minus 1pt, + after skip=8pt plus 4pt minus 2pt + }, + docexample/.style={bicolor, + beforeafter example, + arc is angular,fonttitle=\bfseries, + fontlower=\footnotesize, + colframe=green!25!yellow!50!black, + colback=green!25!yellow!7, + colbacklower=white, + drop fuzzy shadow=green!25!yellow!50!black, + listing engine=minted, + documentation minted style=colorful, + documentation minted options={fontsize=\footnotesize}, + }, +} + +\renewcommand*{\tcbdocnew}[1]{\textcolor{green!50!black}{\sffamily\bfseries N} #1} +\renewcommand*{\tcbdocupdated}[1]{\textcolor{blue!75!black}{\sffamily\bfseries U} #1} + +\NewDocumentCommand{\csvsorter}{}{\textsf{\bfseries\color{red!20!black}CSV-Sorter}} + +\newtcbinputlisting{\csvlisting}[1]{docexample,minted options={fontsize=\footnotesize},minted language=latex, + fonttitle=\bfseries,listing only,title={CSV file \flqq\texttt{\detokenize{#1.csv}}\frqq},listing file=#1.csv} + +\newtcbinputlisting{\xmllisting}[1]{docexample,minted options={fontsize=\footnotesize},minted language=xml, + fonttitle=\bfseries,listing only,title={Configuration file \flqq\texttt{\detokenize{#1.xml}}\frqq},listing file=#1.xml} + +\NewTotalTCBox{\verbbox}{m}{enhanced,on line,size=fbox,frame empty,colback=red!5!white, + colupper=red!85!black,fontupper=\bfseries\ttfamily}{\detokenize{"}#1\detokenize{"}} + +\NewDocumentCommand{\ctanpkg}{m} + {% + \href{https://ctan.org/pkg/#1}{\texttt{#1}}% + } + +\makeindex + +\pdfsuppresswarningpagegroup=1 diff --git a/csvsimple/csvsimple-example.csv b/csvsimple/csvsimple-example.csv new file mode 100644 index 0000000..9a988ac --- /dev/null +++ b/csvsimple/csvsimple-example.csv @@ -0,0 +1,5 @@ +last name,first name,address,zip,telephone,year of birth +Appleby,Anne,Shrimpsbury,SH-124,555-4455,1960 +Brown,Carl,Midsomer Garden,MD-945,555-2423,1982 +Smith,John,Shrimpsbury,SH-123,555-4584,1978 +Underwood,Mary,Oglsby,OG-345,555-3434,1956 diff --git a/csvsimple/csvsimple-example.pdf b/csvsimple/csvsimple-example.pdf new file mode 100644 index 0000000..8915e27 Binary files /dev/null and b/csvsimple/csvsimple-example.pdf differ diff --git a/csvsimple/csvsimple-example.tex b/csvsimple/csvsimple-example.tex new file mode 100644 index 0000000..5d872f5 --- /dev/null +++ b/csvsimple/csvsimple-example.tex @@ -0,0 +1,110 @@ +%% The LaTeX package csvsimple - version 2.5.0 (2023/10/16) +%% csvsimple-example.tex: an example for csvsimple +%% +%% ------------------------------------------------------------------------------------------- +%% Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm +%% ------------------------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `author-maintained'. +%% +%% This work consists of all files listed in README.md +%% +\documentclass{article} +\usepackage{ifthen,array,booktabs} + +\IfFileExists{csvsimple-doc.cfg}{\input{csvsimple-doc.cfg}}{}% ignore this line +\providecommand\csvpkgprefix{} % ignore this line + +\usepackage{\csvpkgprefix csvsimple-l3}% \usepackage{csvsimple-l3} + +\begin{document} + +%---------------------------------------------------------- +\section{Automatic table generation (for testing)} + +{\small +\csvautotabular{csvsimple-example.csv}} + + +%---------------------------------------------------------- +\section{My first CSV table} +\csvreader[tabular=|l|l|, + table head=\hline\multicolumn{2}{|c|}{\bfseries My telephone book}\\\hline + \bfseries Name & \bfseries Number\\\hline\hline, + late after line=\\\hline]% + {csvsimple-example.csv}{last name=\surname,first name=\givenname,telephone=\telephone}{% + \givenname\ \surname & \telephone +} + + +%---------------------------------------------------------- +\section{Remembering the names} +\csvnames{my names}{last name=\surname,first name=\givenname,address=\address,zip=\zip,telephone=\telephone,year of birth=\birthyear} + +\csvreader[my names, late after line=\\, late after last line=]% + {csvsimple-example.csv}{}{% + \givenname\ was born in \birthyear\ and lives in \address. +} + + +%---------------------------------------------------------- +\section{Filter fun} + +\csvreader[my names, filter equal={\address}{Shrimpsbury}, tabbing, + table head=\bfseries Shrimpsbury friends: \=\hspace*{3cm}\=\+\kill, + before first line=\<\bfseries Shrimpsbury friends:\>]% + {csvsimple-example.csv}{}{% + \surname, \givenname \> \telephone +} + + +%---------------------------------------------------------- +\section{More filter fun} + +\csvreader[my names, filter ifthen=\birthyear<1980, centered tabular=rllr, + table head=\multicolumn{4}{c}{\bfseries People born before 1980}\\\toprule + \# & Name & Postal address & input line no.\\\midrule, + late after line=\\, late after last line=\\\bottomrule]% + {csvsimple-example.csv}{}{% + \thecsvrow & \givenname\ \surname & \zip\ \address & \thecsvinputline +} + + +%---------------------------------------------------------- +\section{Again and again} + +\csvstyle{my table}{my names, + centered tabular=|r|l|l|l|, + table head=\hline\multicolumn{4}{|c|}{\bfseries #1}\\\hline + \# & Name & Telephone & Postal address\\\hline\hline, + late after line=\\, late after last line=\\\hline} + +\csvreader[my table=Predefined table]{csvsimple-example.csv}{}{% + \thecsvrow & \givenname\ \surname & \telephone & \zip\ \address +} + +\csvreader[my table=Filtering for Smith, filter equal={\surname}{Smith}]% + {csvsimple-example.csv}{}{% + \thecsvrow & \givenname\ \surname & \telephone & \zip\ \address +} + +\csvstyle{all and everything}{my table=#1, file={csvsimple-example.csv}, + command=\thecsvrow & \givenname\ \surname & \telephone & \zip\ \address} + +\csvloop{all and everything=Loop instead of reader} + +\csvloop{all and everything=With Shrimpsbury filter, filter equal={\address}{Shrimpsbury}} + +\csvloop{all and everything=A little modification, late after line=\\\hline} + + +\end{document} + diff --git a/csvsimple/csvsimple-l3.pdf b/csvsimple/csvsimple-l3.pdf new file mode 100644 index 0000000..9bf62e2 Binary files /dev/null and b/csvsimple/csvsimple-l3.pdf differ diff --git a/csvsimple/csvsimple-l3.sty b/csvsimple/csvsimple-l3.sty new file mode 100644 index 0000000..cb6a676 --- /dev/null +++ b/csvsimple/csvsimple-l3.sty @@ -0,0 +1,1605 @@ +%% The LaTeX package csvsimple - version 2.5.0 (2023/10/16) +%% csvsimple-l3.sty: Simple LaTeX CSV file processing (LaTeX3) +%% +%% ------------------------------------------------------------------------------------------- +%% Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm +%% ------------------------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `author-maintained'. +%% +%% This work consists of all files listed in README.md +%% +\ProvidesExplPackage{csvsimple-l3}{2023/10/16}{2.5.0} + {LaTeX3 CSV file processing} + + + +%---- check package + +\cs_if_exist:NT \c__csvsim_package_expl_bool + { + \msg_new:nnn { csvsimple }{ l3 / package-loaded } + { Package~'csvsimple-legacy'~seems~already~be~loaded!~ + 'csvsimple-l3'~cannot~be~loaded~simultaneously.~ + Therefore,~loading~of~'csvsimple-l3'~stops~now.} + \msg_warning:nn { csvsimple }{ l3 / package-loaded } + \tex_endinput:D + } +\bool_const:Nn \c__csvsim_package_expl_bool { \c_true_bool } + + + +%---- declarations and expl3 variants + +\bool_new:N \g__csvsim_check_column_count_bool +\bool_new:N \g__csvsim_collect_data_bool +\bool_new:N \g__csvsim_colnames_detection_bool +\bool_new:N \g__csvsim_head_bool +\bool_new:N \g__csvsim_head_to_colnames_bool +\bool_new:N \g__csvsim_line_accepted_bool +\bool_new:N \g__csvsim_line_firstline_bool +\bool_new:N \l__csvsim_respect_and_bool +\bool_new:N \l__csvsim_respect_backslash_bool +\bool_new:N \l__csvsim_respect_circumflex_bool +\bool_new:N \l__csvsim_respect_dollar_bool +\bool_new:N \l__csvsim_respect_leftbrace_bool +\bool_new:N \l__csvsim_respect_percent_bool +\bool_new:N \l__csvsim_respect_rightbrace_bool +\bool_new:N \l__csvsim_respect_sharp_bool +\bool_new:N \l__csvsim_respect_tab_bool +\bool_new:N \l__csvsim_respect_tilde_bool +\bool_new:N \l__csvsim_respect_underscore_bool + +\int_new:N \g__csvsim_col_int +\int_new:N \g__csvsim_colmax_int +\int_new:N \g_csvsim_inputline_int +\int_new:N \g_csvsim_row_int +\int_new:N \g_csvsim_columncount_int + +\seq_new:N \g__csvsim_colname_seq +\seq_new:N \g__csvsim_line_seq +\seq_new:N \g__csvsim_range_seq + +\str_new:N \g__csvsim_curfilename_str +\str_new:N \g__csvsim_filename_str +\str_new:N \l__csvsim_csvsorter_command_str +\str_new:N \l__csvsim_csvsorter_configpath_str +\str_new:N \l__csvsim_csvsorter_log_str +\str_new:N \l__csvsim_csvsorter_token_str +\str_new:N \l__csvsim_ppfilename_str +\str_new:N \l__csvsim_temp_filename_str + +\tl_const:Nn \c__csvsim_par_tl { \par } + +\tl_new:N \g__csvsim_after_table_tl +\tl_new:N \g__csvsim_before_table_tl +\tl_new:N \g__csvsim_begin_table_center_tl +\tl_new:N \g__csvsim_body_tl +\tl_new:N \g__csvsim_catcode_tl +\tl_new:N \g__csvsim_collect_tl +\tl_new:N \g__csvsim_columnnames_tl +\tl_new:N \g__csvsim_data_collection_tl +\tl_new:N \g__csvsim_end_table_center_tl +\tl_new:N \g__csvsim_filter_tl +\tl_new:N \g__csvsim_generic_table_options_tl +\tl_new:N \g__csvsim_headname_prefix_tl +\tl_new:N \g__csvsim_hook_after_filter_tl +\tl_new:N \g__csvsim_hook_after_first_line_tl +\tl_new:N \g__csvsim_hook_after_head_tl +\tl_new:N \g__csvsim_hook_after_line_tl +\tl_new:N \g__csvsim_hook_after_reading_tl +\tl_new:N \g__csvsim_hook_before_filter_tl +\tl_new:N \g__csvsim_hook_before_first_line_tl +\tl_new:N \g__csvsim_hook_before_line_tl +\tl_new:N \g__csvsim_hook_before_reading_tl +\tl_new:N \g__csvsim_hook_columncounterror_tl +\tl_new:N \g__csvsim_hook_late_after_first_line_tl +\tl_new:N \g__csvsim_hook_late_after_head_tl +\tl_new:N \g__csvsim_hook_late_after_last_line_tl +\tl_new:N \g__csvsim_hook_late_after_line_tl +\tl_new:N \g__csvsim_hook_late_after_line_application_tl +\tl_new:N \g__csvsim_hook_table_begin_tl +\tl_new:N \g__csvsim_hook_table_end_tl +\tl_new:N \g__csvsim_preprocessor_tl +\tl_new:N \g__csvsim_separator_tl +\tl_new:N \g__csvsim_table_foot_tl +\tl_new:N \g__csvsim_table_head_tl + +\tl_new:N \l__csvsim_filter_condition_tl + + +\group_begin: + \char_set_catcode_other:n { 9 } + \str_const:Nn \c__csvsim_tab_str { ^^I } +\group_end: + +\regex_const:Nn \c__csvsim_integer_regex {\A\d+\Z} + +\cs_generate_variant:Nn \bool_gset:Nn { NV } +\cs_generate_variant:Nn \seq_gset_split:Nnn { NVV } + + +%---- messages + +\msg_new:nnnn { csvsimple }{ column-name } + { Unknown~column~key~'#1'. } + { The~key~'#1'~you~used~in~'column~names'~is~unknown.\\ + Therefore,~the~macro~#2 is~not~defined. + } + +\msg_new:nnn { csvsimple }{ empty-head } + { File~'#1'~starts~with~an~empty~line~(empty~head)!.} + +\msg_new:nnn { csvsimple }{ file-error } + { File~'#1'~not~existent,~not~readable,~or~empty!} + +\msg_new:nnn { csvsimple }{ column-wrong-count } + { #1~instead~of~#2~columns~for~input~line~#3~of~file~'#4'} + +\msg_new:nnn { csvsimple }{ sort-info } + { Sort~'#1'~by~'#2' } + +\msg_new:nnn { csvsimple }{ sort-shell-escape } + { You~need~to~use~'-shell-escape'~to~run~CSV-Sorter } + +\msg_new:nnnn { csvsimple }{ sort-error } + { Call~of~CSV-Sorter~failed! } + { See~log~file~'\l__csvsim_csvsorter_log_str'. } + + + +%---- core loop processing + +\NewHook{csvsimple/csvline} + +\cs_new_protected_nopar:Npn \__csvsim_read_line: + { + \group_begin: + \g__csvsim_catcode_tl + \ior_get:NNTF \g__csvsim_ior \l_tmpa_tl + { + \tl_gset_eq:NN \csvline \l_tmpa_tl + \int_gincr:N \g_csvsim_inputline_int + } + { + \msg_error:nnx { csvsimple }{ file-error }{ \g__csvsim_curfilename_str } + } + \group_end: + \UseHook{csvsimple/csvline} + } + + +\cs_new_protected_nopar:Npn \__csvsim_scan_line: + { + \int_gzero:N \g__csvsim_col_int + \seq_gset_split:NVV \g__csvsim_line_seq \g__csvsim_separator_tl \csvline + \seq_map_inline:Nn \g__csvsim_line_seq + { + \int_gincr:N \g__csvsim_col_int + \tl_gset:cn {csvcol \int_to_roman:n \g__csvsim_col_int}{##1} + } + \int_compare:nNnT \g__csvsim_colmax_int < \g__csvsim_col_int + { + \int_gset_eq:NN \g__csvsim_colmax_int \g__csvsim_col_int + } + \int_compare:nNnT \g_csvsim_columncount_int < \c_one_int + { + \int_gset_eq:NN \g_csvsim_columncount_int \g__csvsim_col_int + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_process_head_name:n #1 + { + \tl_set:No \l_tmpa_tl {\cs:w csvcol\int_to_roman:n{#1} \cs_end:} + \exp_args:NnV \cs_set_nopar:cpn {__csvsim__/\l_tmpa_tl} \l_tmpa_tl + \bool_if:NT \g__csvsim_head_to_colnames_bool + { + \tl_set:No \l_tmpb_tl {\cs:w \g__csvsim_headname_prefix_tl \l_tmpa_tl \cs_end:} + \tl_put_right:NV \l_tmpb_tl \l_tmpa_tl + \exp_args:NNV \seq_gput_right:Nn \g__csvsim_colname_seq \l_tmpb_tl + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_read_head: + { + \__csvsim_read_line: + \tl_if_eq:NNTF \csvline \c__csvsim_par_tl + { + \msg_error:nnx { csvsimple }{ empty-head }{ \g__csvsim_filename_str } + } + { + \int_gzero:N \g_csvsim_columncount_int + \__csvsim_scan_line: + \bool_if:NT \g__csvsim_colnames_detection_bool + { + \int_step_function:nN \g_csvsim_columncount_int \__csvsim_process_head_name:n + } + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_process_colname:nn #1#2 + { + \cs_if_exist:cTF {__csvsim__/#1} + { + \tl_set:Nv \l_tmpa_tl {__csvsim__/#1} + \tl_put_left:Nn \l_tmpa_tl {#2} + \exp_args:NNV \seq_gput_right:Nn \g__csvsim_colname_seq \l_tmpa_tl + } + { + \regex_match:NnTF \c__csvsim_integer_regex {#1} + { + \tl_set:No \l_tmpa_tl {\cs:w csvcol\int_to_roman:n{#1} \cs_end:} + \tl_put_left:Nn \l_tmpa_tl {#2} + \exp_args:NNV \seq_gput_right:Nn \g__csvsim_colname_seq \l_tmpa_tl + } + { + \str_set:Nn \l_tmpb_str {#2} + \msg_error:nnxx { csvsimple }{ column-name }{ #1 }{ \l_tmpb_str } + } + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_set_colnames: + { + \seq_map_inline:Nn \g__csvsim_colname_seq + { + \tl_gset_eq:NN ##1 + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_loop: + { + % preprocess + \tl_if_empty:NTF \l__csvsim_preprocessor_tl + { + \str_gset_eq:NN \g__csvsim_curfilename_str \g__csvsim_filename_str + } + { + \l__csvsim_preprocessor_tl \g__csvsim_filename_str \l__csvsim_ppfilename_str + \str_gset_eq:NN \g__csvsim_curfilename_str \l__csvsim_ppfilename_str + } + + % initialize + \cs_if_exist:NF \g__csvsim_ior + { + \ior_new:N \g__csvsim_ior + } + \__csvsim_setup_catcode_list: + \seq_gclear:N \g__csvsim_colname_seq + \int_gzero:N \g_csvsim_inputline_int + \int_gzero:N \g_csvsim_row_int + \int_gset_eq:NN \g__csvsim_colmax_int \c_one_int + \bool_if:NT \g__csvsim_collect_data_bool + { + \__csvsim_collect_data: + } + + % open file + \g__csvsim_hook_before_reading_tl + \g__csvsim_hook_table_begin_tl + \ior_open:Nn \g__csvsim_ior { \g__csvsim_curfilename_str } + + % read head line + \bool_if:NT \g__csvsim_head_bool + { + \__csvsim_read_head: + } + \exp_args:NNNV \keyval_parse:NNn \use_none:n + \__csvsim_process_colname:nn \g__csvsim_columnnames_tl + \bool_if:NT \g__csvsim_head_bool + { + \g__csvsim_hook_after_head_tl + } + + % read body lines + \tl_gset:Nn \g__csvsim_hook_late_after_line_application_tl + { + \g__csvsim_hook_late_after_first_line_tl + \tl_gset_eq:NN \g__csvsim_hook_late_after_line_application_tl \g__csvsim_hook_late_after_line_tl + } + \bool_gset_true:N \g__csvsim_line_firstline_bool + \bool_until_do:nn {\ior_if_eof_p:N \g__csvsim_ior} + { + \__csvsim_read_line: + \tl_if_eq:NNF \csvline \c__csvsim_par_tl + { + \bool_gset_true:N \g__csvsim_line_accepted_bool + \__csvsim_scan_line: + \__csvsim_set_colnames: + \bool_if:NT \g__csvsim_check_column_count_bool + { + \int_compare:nNnF \g__csvsim_col_int = \g_csvsim_columncount_int + { + \bool_gset_false:N \g__csvsim_line_accepted_bool + \g__csvsim_hook_columncounterror_tl + } + } + \bool_if:NT \g__csvsim_line_accepted_bool + { + \g__csvsim_hook_before_filter_tl + \g__csvsim_filter_tl + \bool_if:NT \g__csvsim_line_accepted_bool + { + \int_gincr:N \g_csvsim_row_int + \__csvsim_check_range: + \bool_if:NT \g__csvsim_line_accepted_bool + { + \bool_if:NTF \g__csvsim_line_firstline_bool + { + \bool_if:NT \g__csvsim_head_bool + { + \g__csvsim_hook_late_after_head_tl + } + \g__csvsim_hook_after_filter_tl + \g__csvsim_hook_before_first_line_tl + \g__csvsim_body_tl + \g__csvsim_hook_after_first_line_tl + \bool_gset_false:N \g__csvsim_line_firstline_bool + } + { + \g__csvsim_hook_late_after_line_application_tl + \g__csvsim_hook_after_filter_tl + \g__csvsim_hook_before_line_tl + \g__csvsim_body_tl + \g__csvsim_hook_after_line_tl + } + } + } + } + } + } + + % close file + \ior_close:N \g__csvsim_ior + + % clear macros + \int_step_inline:nn \g__csvsim_colmax_int + { + \tl_set:No \l_tmpa_tl {\cs:w csvcol\int_to_roman:n{##1} \cs_end:} + \use:x + { + \exp_not:N\tl_gclear:N \exp_not:V\l_tmpa_tl + } + } + \__csvsim_set_colnames: + \seq_gclear:N \g__csvsim_colname_seq + \bool_if:NF \g__csvsim_line_firstline_bool + { + \g__csvsim_hook_late_after_last_line_tl + } + \g__csvsim_hook_table_end_tl + \g__csvsim_hook_after_reading_tl + \bool_if:NT \g__csvsim_collect_data_bool + { + \exp_args:NV \tl_gset_eq:NN \g__csvsim_data_collection_tl \g__csvsim_collect_tl + \tl_gclear:N \g__csvsim_collect_tl + } + } + + +\NewDocumentCommand \csvloop { +m } + { + \keys_set:nn { csvsim } { default, every~csv, #1} + \__csvsim_loop: + } + + +\NewDocumentCommand \csvreader { +O{} m m +m } + { + \keys_set:nn { csvsim } { default, every~csv, #1, file={#2}, column~names={#3} } + \tl_gset:Nn \g__csvsim_body_tl {#4} + \__csvsim_loop: + } + + + +%---- auxiliary user macros + +% \NewDocumentCommand \csvlinetotablerow { } + % { + % \tl_clear:N \l_tmpa_tl + % \bool_set_false:N \l_tmpa_bool + % \seq_map_inline:Nn \g__csvsim_line_seq + % { + % \bool_if:NTF \l_tmpa_bool + % { + % \tl_put_right:Nn \l_tmpa_tl { & ##1 } + % } + % { + % \tl_put_right:Nn \l_tmpa_tl { ##1 } + % \bool_set_true:N \l_tmpa_bool + % } + % } + % \l_tmpa_tl + % } + + +\NewExpandableDocumentCommand \csvlinetotablerow { } + { + \seq_use:Nn \g__csvsim_line_seq { & } + } + + +\NewExpandableDocumentCommand \thecsvrow { } + { + \int_use:N \g_csvsim_row_int + } + + +\NewExpandableDocumentCommand \thecsvcolumncount { } + { + \int_use:N \g_csvsim_columncount_int + } + + +\NewExpandableDocumentCommand \thecsvinputline { } + { + \int_use:N \g_csvsim_inputline_int + } + + +\NewExpandableDocumentCommand \ifcsvfirstrow { } + { + \bool_if:NTF \g__csvsim_line_firstline_bool + } + +% deprecated +\NewExpandableDocumentCommand \csviffirstrow { } + { + \bool_if:NTF \g__csvsim_line_firstline_bool + } + + +\NewExpandableDocumentCommand \ifcsvoddrow { } + { + \int_if_odd:nTF {\g_csvsim_row_int} + } + +% deprecated +\NewExpandableDocumentCommand \csvifoddrow { } + { + \int_if_odd:nTF {\g_csvsim_row_int} + } + + +\NewExpandableDocumentCommand \ifcsvstrcmp { m m } + { + \str_compare:eNeTF {#1} = {#2} + } + + +\NewExpandableDocumentCommand \ifcsvnotstrcmp { m m +m +m } + { + \ifcsvstrcmp{#1}{#2}{#4}{#3} + } + + +\NewDocumentCommand \ifcsvstrequal { m m } + { + \tl_set:Nx \l_tmpa_tl {#1} + \tl_set:Nx \l_tmpb_tl {#2} + \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl + } + + +\NewDocumentCommand \ifcsvprostrequal { m m } + { + \protected@edef \l_tmpa_tl {#1} + \protected@edef \l_tmpb_tl {#2} + \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl + } + + +\NewExpandableDocumentCommand \ifcsvfpcmp { m } + { + \fp_compare:nTF {#1} + } + + +\NewExpandableDocumentCommand \ifcsvintcmp { m } + { + \int_compare:nTF {#1} + } + + +%---- filename functions + +\cs_new_protected_nopar:Npn \__csvsim_set_temp_filename:nnn #1#2#3 + { + \str_set:Nn \l__csvsim_temp_filename_str {#2#3} + \str_if_empty:NTF \l__csvsim_temp_filename_str + { + \str_set:Nn \l__csvsim_temp_filename_str {#1} + } + { + \str_set:Nn \l_tmpa_str {#1} + \str_if_empty:NF \l_tmpa_str + { + \str_compare:eNeF { \str_item:Nn \l_tmpa_str {-1} } = { / } + { + \str_put_right:Nn \l_tmpa_str {/} + } + \str_concat:NNN \l__csvsim_temp_filename_str + \l_tmpa_str \l__csvsim_temp_filename_str + } + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_set_temp_filename:n #1 + { + \file_parse_full_name_apply:nN { #1 } \__csvsim_set_temp_filename:nnn + } + + +\cs_new_protected_nopar:Npn \__csvsim_set_filename:Nn #1#2 + { + \__csvsim_set_temp_filename:n { #2 } + \str_set_eq:NN #1 \l__csvsim_temp_filename_str + } + + +\cs_new_protected_nopar:Npn \__csvsim_gset_filename:Nn #1#2 + { + \__csvsim_set_temp_filename:n { #2 } + \str_gset_eq:NN #1 \l__csvsim_temp_filename_str + } + + + +%---- keys + +\NewDocumentCommand \csvset { +m } + { + \keys_set:nn { csvsim } { #1 } + } + + +\NewDocumentCommand \csvstyle { m +m } + { + \keys_define:nn { csvsim } + { + #1 .meta:n = { #2 } + } + } + + +\NewDocumentCommand \csvnames { m m } + { + \keys_define:nn { csvsim } + { + #1 .meta:n = { column~names={#2} } + } + } + + +\keys_define:nn { csvsim } + { + file .code:n = \__csvsim_gset_filename:Nn \g__csvsim_filename_str {#1}, + column~names~reset .code:n = \tl_gclear:N \g__csvsim_columnnames_tl, + column~names .code:n = + { + \tl_if_empty:NTF \g__csvsim_columnnames_tl + { + \tl_gset:Nn \g__csvsim_columnnames_tl {#1} + } + { + \tl_gput_right:Nn \g__csvsim_columnnames_tl {,#1} + } + }, + command .tl_gset:N = \g__csvsim_body_tl, + check~column~count .bool_gset:N = \g__csvsim_check_column_count_bool, + on~column~count~error .tl_gset:N = \g__csvsim_hook_columncounterror_tl, + head .bool_gset:N = \g__csvsim_head_bool, + head~to~column~names~prefix .tl_gset:N = \g__csvsim_headname_prefix_tl, + head~to~column~names .bool_gset:N = \g__csvsim_head_to_colnames_bool, + column~names~detection .bool_gset:N = \g__csvsim_colnames_detection_bool, + column~count .int_gset:N = \g_csvsim_columncount_int, + separator .choice:, + separator/comma .code:n = + { + \tl_gset:Nn \g__csvsim_separator_tl {,} + }, + separator/semicolon .code:n = + { + \tl_gset:Nn \g__csvsim_separator_tl {;} + }, + separator/pipe .code:n = + { + \tl_gset:Nn \g__csvsim_separator_tl {|} + }, + separator/tab .code:n = + { + \tl_gset:NV \g__csvsim_separator_tl \c__csvsim_tab_str + \csvset{respect~tab} + }, + separator/space .code:n = + { + \tl_gset:Nn \g__csvsim_separator_tl {~} + }, + every~csv .meta:n = {}, + no~head .meta:n = { head=false }, + no~check~column~count .meta:n = { check~column~count=false }, + warn~on~column~count~error .meta:n = { on~column~count~error= + { + \msg_warning:nnxxxx { csvsimple }{ column-wrong-count } + { \int_use:N\g__csvsim_col_int } + { \int_use:N\g_csvsim_columncount_int } + { \int_use:N\g_csvsim_inputline_int } + { \g__csvsim_filename_str } + }}, + } + + +%---- hooks + +\keys_define:nn { csvsim } + { + before~reading .tl_gset:N = \g__csvsim_hook_before_reading_tl, + after~head .tl_gset:N = \g__csvsim_hook_after_head_tl, + before~filter .tl_gset:N = \g__csvsim_hook_before_filter_tl, + after~filter .tl_gset:N = \g__csvsim_hook_after_filter_tl, + late~after~head .tl_gset:N = \g__csvsim_hook_late_after_head_tl, + late~after~first~line .tl_gset:N = \g__csvsim_hook_late_after_first_line_tl, + late~after~last~line .tl_gset:N = \g__csvsim_hook_late_after_last_line_tl, + before~first~line .tl_gset:N = \g__csvsim_hook_before_first_line_tl, + after~first~line .tl_gset:N = \g__csvsim_hook_after_first_line_tl, + after~reading .tl_gset:N = \g__csvsim_hook_after_reading_tl, + late~after~line .code:n = + { + \tl_gset:Nn \g__csvsim_hook_late_after_line_tl {#1} + \tl_gset_eq:NN \g__csvsim_hook_late_after_first_line_tl \g__csvsim_hook_late_after_line_tl + \tl_gset_eq:NN \g__csvsim_hook_late_after_last_line_tl \g__csvsim_hook_late_after_line_tl + }, + before~line .code:n = + { + \tl_gset:Nn \g__csvsim_hook_before_line_tl {#1} + \tl_gset_eq:NN \g__csvsim_hook_before_first_line_tl \g__csvsim_hook_before_line_tl + }, + after~line .code:n = + { + \tl_gset:Nn \g__csvsim_hook_after_line_tl {#1} + \tl_gset_eq:NN \g__csvsim_hook_after_first_line_tl \g__csvsim_hook_after_line_tl + }, + } + + +%---- filter + +\cs_new_protected_nopar:Npn \__csvsim_set_filter:n #1 + { + \tl_gset:Nn \g__csvsim_filter_tl + { + #1 + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_set_filter_bool:n #1 + { + \tl_set:Nn \l__csvsim_filter_condition_tl { #1 } + \tl_gset:Nn \g__csvsim_filter_tl + { + \bool_gset:NV \g__csvsim_line_accepted_bool \l__csvsim_filter_condition_tl + } + } + + +\NewDocumentCommand \csvfilteraccept { } + { + \__csvsim_set_filter:n + { + \bool_gset_true:N \g__csvsim_line_accepted_bool + } + } + + +\NewDocumentCommand \csvfilterreject { } + { + \__csvsim_set_filter:n + { + \bool_gset_false:N \g__csvsim_line_accepted_bool + } + } + + +\keys_define:nn { csvsim } + { + no~filter .code:n = + { + \csvfilteraccept + }, + filter~reject~all .code:n = + { + \csvfilterreject + }, + filter~accept~all .code:n = + { + \csvfilteraccept + }, + full~filter. tl_gset:N = \g__csvsim_hook_before_filter_tl, + filter~test .code:n = + { + \__csvsim_set_filter:n + { + #1 + { \bool_gset_true:N \g__csvsim_line_accepted_bool } + { \bool_gset_false:N \g__csvsim_line_accepted_bool } + } + }, + filter~bool .code:n = + { + \__csvsim_set_filter_bool:n { #1 } + }, + filter~fp .code:n = + { + \__csvsim_set_filter_bool:n { \fp_compare_p:n {#1} } + }, + filter~strcmp .code:n = + { + \__csvsim_set_filter_bool:n { \str_if_eq_p:ee #1 } + }, + filter~not~strcmp .code:n = + { + \__csvsim_set_filter_bool:n { !\str_if_eq_p:ee #1 } + }, + and~filter~bool .code:n = + { + \tl_put_right:Nn \l__csvsim_filter_condition_tl { && #1 } + }, + and~filter~fp .code:n = + { + \tl_put_right:Nn \l__csvsim_filter_condition_tl { && \fp_compare_p:n {#1} } + }, + and~filter~strcmp .code:n = + { + \tl_put_right:Nn \l__csvsim_filter_condition_tl { && \str_if_eq_p:ee #1 } + }, + and~filter~not~strcmp .code:n = + { + \tl_put_right:Nn \l__csvsim_filter_condition_tl { && !\str_if_eq_p:ee #1 } + }, + or~filter~bool .code:n = + { + \tl_put_right:Nn \l__csvsim_filter_condition_tl { || #1 } + }, + or~filter~fp .code:n = + { + \tl_put_right:Nn \l__csvsim_filter_condition_tl { || \fp_compare_p:n {#1} } + }, + or~filter~strcmp .code:n = + { + \tl_put_right:Nn \l__csvsim_filter_condition_tl { || \str_if_eq_p:ee #1 } + }, + or~filter~not~strcmp .code:n = + { + \tl_put_right:Nn \l__csvsim_filter_condition_tl { || !\str_if_eq_p:ee #1 } + }, + } + + +\NewDocumentCommand \csvfilterbool { m m } + { + \keys_define:nn { csvsim } + { + #1 .meta:n = { filter~bool={#2} } + } + } + + +% ifthen +\keys_define:nn { csvsim } + { + filter~ifthen .code:n = + { + \__csvsim_set_filter:n + { + \ifthenelse{#1} + { \bool_gset_true:N \g__csvsim_line_accepted_bool } + { \bool_gset_false:N \g__csvsim_line_accepted_bool } + } + }, + filter~equal .meta:n = { filter~ifthen=\equal #1 }, + filter~not~equal .meta:n = { filter~ifthen=\not\equal #1 }, + } + + +% etoolbox +\keys_define:nn { csvsim } + { + filter~expr .code:n = + { + \__csvsim_set_filter:n + { + \ifboolexpr{#1} + { \bool_gset_true:N \g__csvsim_line_accepted_bool } + { \bool_gset_false:N \g__csvsim_line_accepted_bool } + } + }, + } + + + +%---- range + + +\cs_new_protected_nopar:Npn \__csvsim_add_range:n #1 + { + \tl_if_in:nnTF {#1}{-} + { + \seq_set_split:Nnn \l_tmpa_seq {-} {#1} + \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl + \seq_pop_left:NN \l_tmpa_seq \l_tmpb_tl + \tl_if_empty:NTF \l_tmpa_tl + { + \int_set_eq:NN \l_tmpa_int \c_one_int + } + { + \int_set:Nn \l_tmpa_int { \l_tmpa_tl } + } + \tl_if_empty:NTF \l_tmpb_tl + { + \int_set_eq:NN \l_tmpb_int \c_max_int + } + { + \int_set:Nn \l_tmpb_int { \l_tmpb_tl } + } + } + { + \tl_if_in:nnTF {#1}{+} + { + \seq_set_split:Nnn \l_tmpa_seq {+} {#1} + \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl + \seq_pop_left:NN \l_tmpa_seq \l_tmpb_tl + \tl_if_empty:NTF \l_tmpa_tl + { + \int_set:Nn \l_tmpa_int { 1 } + } + { + \int_set:Nn \l_tmpa_int { \l_tmpa_tl } + } + \tl_if_empty:NTF \l_tmpb_tl + { + \int_set_eq:NN \l_tmpb_int \l_tmpa_int + } + { + \int_set:Nn \l_tmpb_int { \l_tmpa_int + \l_tmpb_tl - 1 } + } + } + { + \int_set:Nn \l_tmpa_int {#1} + \int_set_eq:NN \l_tmpb_int \l_tmpa_int + } + } + \seq_gput_right:Nx \g__csvsim_range_seq {{\int_use:N \l_tmpa_int}{\int_use:N \l_tmpb_int}} + } + + +\cs_new_protected_nopar:Npn \__csvsim_set_range:n #1 + { + \seq_gclear:N \g__csvsim_range_seq + \keyval_parse:NNn + \__csvsim_add_range:n + \use_none:nn + { #1 } + } + +\cs_generate_variant:Nn \__csvsim_set_range:n { e } + +\keys_define:nn { csvsim } + { + range .code:n = + { + \__csvsim_set_range:e {#1} + }, + } + + +\prg_new_conditional:Npnn \__csvsim_if_in_range:nn #1#2 { p , T } + { + \if_int_compare:w #1 > \g_csvsim_row_int + \prg_return_false: + \else: + \if_int_compare:w #2 < \g_csvsim_row_int + \prg_return_false: + \else: + \prg_return_true: + \fi: + \fi: + } + + +\cs_new_protected_nopar:Npn \__csvsim_check_range: + { + \seq_if_empty:NF \g__csvsim_range_seq + { + \bool_gset_false:N \g__csvsim_line_accepted_bool + \seq_map_inline:Nn \g__csvsim_range_seq + { + \__csvsim_if_in_range:nnT ##1 + { + \bool_gset_true:N \g__csvsim_line_accepted_bool + \seq_map_break: + } + } + } + } + + + +%---- data collection + +\cs_new_protected_nopar:Npn \__csvsim_gset_tl_to_collect:N #1 + { + \tl_gset:Nx #1 + { + \exp_not:N \tl_gput_right:Nn + \exp_not:N \g__csvsim_collect_tl + {\exp_not:V #1} + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_gset_tl_to_collect_expanded:N #1 + { + \tl_gset:Nx #1 + { + \exp_not:N \tl_gput_right:Nx + \exp_not:N \g__csvsim_collect_tl + {\exp_not:V #1} + } + } + + +\cs_new_protected_nopar:Npn \__csvsim_collect_data: + { + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_after_head_tl + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_after_first_line_tl + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_after_line_tl + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_before_first_line_tl + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_before_line_tl + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_late_after_first_line_tl + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_late_after_head_tl + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_late_after_last_line_tl + \__csvsim_gset_tl_to_collect:N \g__csvsim_hook_late_after_line_tl + \__csvsim_gset_tl_to_collect_expanded:N \g__csvsim_body_tl + \tl_gclear:N \g__csvsim_collect_tl + } + + +\cs_set_eq:NN \csvexpval \exp_not:V +\cs_set_eq:NN \csvexpnot \exp_not:N + + +\NewDocumentCommand{ \csvcollectn }{ +m } + { + \tl_gput_right:Nn \g__csvsim_collect_tl {#1} + } + + +\NewDocumentCommand{ \csvcollectx }{ +m } + { + \tl_gput_right:Nx \g__csvsim_collect_tl {#1} + } + + +\NewDocumentCommand{ \csvcollectV }{ m } + { + \tl_gput_right:NV \g__csvsim_collect_tl #1 + } + + +\keys_define:nn { csvsim } + { + collect~data .bool_gset:N = \g__csvsim_collect_data_bool, + data~collection .tl_gset:N = \g__csvsim_data_collection_tl, + } + + +\keys_set:nn { csvsim } + { + data~collection = \csvdatacollection, + } + + + +%---- catcodes + +\cs_new_protected_nopar:Npn \__csvsim_setup_catcode_list: + { + \tl_gclear:N \g__csvsim_catcode_tl + \bool_if:NT \l__csvsim_respect_tab_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 9 } } + } + \bool_if:NT \l__csvsim_respect_percent_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 37 } } + } + \bool_if:NT \l__csvsim_respect_sharp_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 35 } } + } + \bool_if:NT \l__csvsim_respect_dollar_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 36 } } + } + \bool_if:NT \l__csvsim_respect_and_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 38 } } + } + \bool_if:NT \l__csvsim_respect_backslash_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 92 } } + } + \bool_if:NT \l__csvsim_respect_underscore_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 95 } } + } + \bool_if:NT \l__csvsim_respect_tilde_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 126 } } + } + \bool_if:NT \l__csvsim_respect_circumflex_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 94 } } + } + \bool_if:NT \l__csvsim_respect_leftbrace_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 123 } } + } + \bool_if:NT \l__csvsim_respect_rightbrace_bool + { + \tl_gput_right:Nn \g__csvsim_catcode_tl { \char_set_catcode_other:n { 125 } } + } + } + + +\keys_define:nn { csvsim } + { + respect~tab .bool_set:N = \l__csvsim_respect_tab_bool, + respect~percent .bool_set:N = \l__csvsim_respect_percent_bool, + respect~sharp .bool_set:N = \l__csvsim_respect_sharp_bool, + respect~dollar .bool_set:N = \l__csvsim_respect_dollar_bool, + respect~and .bool_set:N = \l__csvsim_respect_and_bool, + respect~backslash .bool_set:N = \l__csvsim_respect_backslash_bool, + respect~underscore .bool_set:N = \l__csvsim_respect_underscore_bool, + respect~tilde .bool_set:N = \l__csvsim_respect_tilde_bool, + respect~circumflex .bool_set:N = \l__csvsim_respect_circumflex_bool, + respect~leftbrace .bool_set:N = \l__csvsim_respect_leftbrace_bool, + respect~rightbrace .bool_set:N = \l__csvsim_respect_rightbrace_bool, + respect~all .meta:n = + { + respect~tab, respect~percent, respect~sharp, respect~dollar, + respect~and, respect~backslash, respect~underscore, respect~tilde, + respect~circumflex, respect~leftbrace, respect~rightbrace + }, + respect~none .meta:n = + { + respect~tab=false, respect~percent=false, respect~sharp=false, + respect~dollar=false, respect~and=false, respect~backslash=false, + respect~underscore=false, respect~tilde=false, respect~circumflex=false, + respect~leftbrace=false, respect~rightbrace=false + }, + } + + + +%---- tables + +\cs_new_protected_nopar:Npn \__csvsim_key_table:nn #1#2 + { + \tl_gset:Nn \g__csvsim_hook_table_begin_tl {#1} + \tl_gset:Nn \g__csvsim_hook_table_end_tl {#2} + } + + +\keys_define:nn { csvsim } + { + before~table .tl_gset:N = \g__csvsim_before_table_tl, + after~table .tl_gset:N = \g__csvsim_after_table_tl, + table~head .tl_gset:N = \g__csvsim_table_head_tl, + table~foot .tl_gset:N = \g__csvsim_table_foot_tl, + generic~table~options .tl_gset:N = \g__csvsim_generic_table_options_tl, + table~centered .choice:, + table~centered .default:n = true, + table~centered/true .code:n = + { + \tl_gset:Nn \g__csvsim_begin_table_center_tl {\begin{center}} + \tl_gset:Nn \g__csvsim_end_table_center_tl {\end{center}} + }, + table~centered/false .code:n = + { + \tl_gclear:N \g__csvsim_begin_table_center_tl + \tl_gclear:N \g__csvsim_end_table_center_tl + }, + _table_ .code:n = \__csvsim_key_table:nn #1, + no~table .meta:n = + { + _table_ = {}{}, + generic~table~options = , + table~centered = false, + }, + generic~table .meta:n = + { + _table_ = + { + \g__csvsim_begin_table_center_tl + \g__csvsim_before_table_tl + \tl_gset:Nn \g_tmpa_tl {\begin{#1}} + \tl_gput_right:NV \g_tmpa_tl \g__csvsim_generic_table_options_tl + \g_tmpa_tl + \g__csvsim_table_head_tl + } + { + \g__csvsim_table_foot_tl + \end{#1} + \g__csvsim_after_table_tl + \g__csvsim_end_table_center_tl + }, + late~after~line = \\ + }, + generic~collected~table .meta:n = + { + collect~data, + _table_ = + { + \tl_gput_right:NV \g__csvsim_collect_tl \g__csvsim_begin_table_center_tl + \tl_gput_right:NV \g__csvsim_collect_tl \g__csvsim_before_table_tl + \tl_gput_right:Nn \g__csvsim_collect_tl { \begin{#1} } + \tl_gput_right:NV \g__csvsim_collect_tl \g__csvsim_generic_table_options_tl + \tl_gput_right:NV \g__csvsim_collect_tl \g__csvsim_table_head_tl + } + { + \tl_gput_right:NV \g__csvsim_collect_tl \g__csvsim_table_foot_tl + \tl_gput_right:Nn \g__csvsim_collect_tl { \end{#1} } + \tl_gput_right:NV \g__csvsim_collect_tl \g__csvsim_after_table_tl + \tl_gput_right:NV \g__csvsim_collect_tl \g__csvsim_end_table_center_tl + }, + late~after~line = \\, + after~reading = + { + \tl_use:N \g__csvsim_collect_tl + }, + }, + } + + +\keys_define:nn { csvsim } + { + tabular .meta:n = + { + generic~table = tabular, + generic~table~options = {{#1}}, + }, + centered~tabular .meta:n = + { + tabular = {#1}, table~centered + }, + longtable .meta:n = + { + generic~table = longtable, + generic~table~options = {{#1}}, + }, + tabbing .meta:n = + { + generic~table = tabbing, + generic~table~options =, + late~after~last~line = + }, + centered tabbing .meta:n = + { + tabbing, table~centered + }, + tabularray .meta:n = + { + generic~collected~table = tblr, + generic~table~options = {{#1}}, + }, + centered~tabularray .meta:n = + { + tabularray = {#1}, table~centered + }, + long~tabularray .meta:n = + { + generic~collected~table = longtblr, + generic~table~options = {{#1}}, + }, + } + + +\keys_define:nn { csvsim } + { + _autotab_ .meta:n = + { + file = #1, + late~after~line = \\, + command = \csvlinetotablerow + }, + _autotabular_ .meta:n = + { + _autotab_ = #1, + late~after~last~line = \g__csvsim_table_foot_tl + \end{tabular} + \g__csvsim_after_table_tl, + }, + autotabular .meta:n = + { + _autotabular_ = #1, + head, + column~names~detection = false, + after~head = \g__csvsim_before_table_tl + \begin{tabular}{|*{\int_use:N\g_csvsim_columncount_int}{l|}} + \g__csvsim_table_head_tl, + table~head = \hline\csvlinetotablerow\\\hline, + table~foot = \\\hline, + }, + autotabular* .meta:n = + { + _autotabular_ = #1, + no~head, + before~first~line = \g__csvsim_before_table_tl + \begin{tabular}{|*{\int_use:N\g_csvsim_columncount_int}{l|}} + \g__csvsim_table_head_tl, + table~head = \hline, + table~foot = \\\hline, + }, + autobooktabular .meta:n = + { + _autotabular_ = #1, + head, + column~names~detection = false, + after~head = \g__csvsim_before_table_tl + \begin{tabular}{*{\int_use:N\g_csvsim_columncount_int}{l}} + \g__csvsim_table_head_tl, + table~head = \toprule\csvlinetotablerow\\\midrule, + table~foot = \\\bottomrule, + }, + autobooktabular* .meta:n = + { + _autotabular_ = #1, + no~head, + before~first~line = \g__csvsim_before_table_tl + \begin{tabular}{*{\int_use:N\g_csvsim_columncount_int}{l}} + \g__csvsim_table_head_tl, + table~head = \toprule, + table~foot = \\\bottomrule, + }, + _autolongtable_ .meta:n = + { + _autotab_ = #1, + late~after~last~line = \end{longtable} + \g__csvsim_after_table_tl, + }, + autolongtable .meta:n = + { + _autolongtable_ = #1, + head, + column~names~detection = false, + after~head = \g__csvsim_before_table_tl + \begin{longtable}{|*{\int_use:N\g_csvsim_columncount_int}{l|}} + \g__csvsim_table_head_tl, + table~head = \hline\csvlinetotablerow\\\hline\endhead + \hline\endfoot, + }, + autolongtable* .meta:n = + { + _autolongtable_ = #1, + no~head, + before~first~line = \g__csvsim_before_table_tl + \begin{longtable}{|*{\int_use:N\g_csvsim_columncount_int}{l|}} + \g__csvsim_table_head_tl, + table~head = \hline\endhead + \hline\endfoot, + }, + autobooklongtable .meta:n = + { + _autolongtable_ = #1, + head, + column~names~detection = false, + after~head = \g__csvsim_before_table_tl + \begin{longtable}{*{\int_use:N\g_csvsim_columncount_int}{l}} + \g__csvsim_table_head_tl, + table~head = \toprule\csvlinetotablerow\\\midrule\endhead + \bottomrule\endfoot, + }, + autobooklongtable* .meta:n = + { + _autolongtable_ = #1, + no~head, + before~first~line = \g__csvsim_before_table_tl + \begin{longtable}{*{\int_use:N\g_csvsim_columncount_int}{l}} + \g__csvsim_table_head_tl, + table~head = \toprule\endhead + \bottomrule\endfoot, + }, + _autotabularray_ .meta:n = + { + file = {#1}, + command = \csvlinetotablerow, + no~head, + generic~collected~table = tblr, + }, + autotabularray .meta:n = + { + _autotabularray_ = {#1}, + generic~table~options = + { { + row{1} = {font=\bfseries}, + hline{1,Z} = {0.8pt}, + hline{2} = {0.4pt}, + } } + }, + autotabularray* .meta:n = + { + _autotabularray_ = {#1}, + generic~table~options = + { { + hline{1,Z} = {0.8pt}, + } } + }, + _autolongtabularray_ .meta:n = + { + file = {#1}, + command = \csvlinetotablerow, + no~head, + generic~collected~table = longtblr, + }, + autolongtabularray .meta:n = + { + _autolongtabularray_ = {#1}, + generic~table~options = + { { + row{1} = {font=\bfseries}, + hline{1,Z} = {0.8pt}, + hline{2} = {0.4pt}, + } } + }, + autolongtabularray* .meta:n = + { + _autolongtabularray_ = {#1}, + generic~table~options = + { { + hline{1,Z} = {0.8pt}, + } } + }, + } + + +\NewDocumentCommand \csvautotabular { s +O{} m } + { + \IfBooleanTF {#1} + { + \keys_set:nn { csvsim } { default, every~csv, autotabular*={#3}, #2} + } + { + \keys_set:nn { csvsim } { default, every~csv, autotabular={#3}, #2} + } + \__csvsim_loop: + } + + +\NewDocumentCommand \csvautolongtable { s +O{} m } + { + \IfBooleanTF {#1} + { + \keys_set:nn { csvsim } { default, every~csv, autolongtable*={#3}, #2} + } + { + \keys_set:nn { csvsim } { default, every~csv, autolongtable={#3}, #2} + } + \__csvsim_loop: + } + + +\NewDocumentCommand \csvautobooktabular { s +O{} m } + { + \IfBooleanTF {#1} + { + \keys_set:nn { csvsim } { default, every~csv, autobooktabular*={#3}, #2} + } + { + \keys_set:nn { csvsim } { default, every~csv, autobooktabular={#3}, #2} + } + \__csvsim_loop: + } + + +\NewDocumentCommand \csvautobooklongtable { s +O{} m } + { + \IfBooleanTF {#1} + { + \keys_set:nn { csvsim } { default, every~csv, autobooklongtable*={#3}, #2} + } + { + \keys_set:nn { csvsim } { default, every~csv, autobooklongtable={#3}, #2} + } + \__csvsim_loop: + } + + +\NewDocumentCommand \csvautotabularray { s +O{} m +o +o } + { + \keys_set:nn { csvsim } { default, every~csv } + \IfBooleanTF {#1} + { + \keys_set:nn { csvsim } { autotabularray*={#3}, #2} + } + { + \keys_set:nn { csvsim } { autotabularray={#3}, #2} + } + \IfNoValueF {#4} + { + \IfNoValueTF {#5} + { + \keys_set:nn { csvsim } { generic~table~options = { { #4 } } } + } + { + \keys_set:nn { csvsim } { generic~table~options = { [ #4 ]{ #5 } } } + } + } + \__csvsim_loop: + } + + +\NewDocumentCommand \csvautolongtabularray { s +O{} m +o +o } + { + \keys_set:nn { csvsim } { default, every~csv } + \IfBooleanTF {#1} + { + \keys_set:nn { csvsim } { autolongtabularray*={#3}, #2} + } + { + \keys_set:nn { csvsim } { autolongtabularray={#3}, #2} + } + \IfNoValueF {#4} + { + \IfNoValueTF {#5} + { + \keys_set:nn { csvsim } { generic~table~options = { { #4 } } } + } + { + \keys_set:nn { csvsim } { generic~table~options = { [ #4 ]{ #5 } } } + } + } + \__csvsim_loop: + } + + +%---- sorting + +\cs_new_protected_nopar:Npn \__csvsim_key_new_sorting_rule:nn #1#2 + { + \keys_define:nn { csvsim } + { + sort~by~#1 .meta:n = { sort~by={#2} }, + } + } + + +\NewDocumentCommand \csvsortingrule { } + { + \__csvsim_key_new_sorting_rule:nn + } + + +\keys_define:nn { csvsim } + { + preprocessor .tl_set:N = \l__csvsim_preprocessor_tl, + preprocessed~file .code:n = \__csvsim_set_filename:Nn \l__csvsim_ppfilename_str {#1}, + csvsorter~command .code:n = \__csvsim_set_filename:Nn \l__csvsim_csvsorter_command_str {#1}, + csvsorter~configpath .code:n = \__csvsim_set_filename:Nn\l__csvsim_csvsorter_configpath_str {#1}, + csvsorter~log .code:n = \__csvsim_set_filename:Nn \l__csvsim_csvsorter_log_str {#1}, + csvsorter~token .code:n = \__csvsim_set_filename:Nn \l__csvsim_csvsorter_token_str {#1}, + no~preprocessing .meta:n = { preprocessor= }, + sort~by .meta:n = + { + preprocessor= + { + \__csvsim_processor_csvsorter:nnn {#1} + } + }, + new~sorting~rule .code:n = \__csvsim_key_new_sorting_rule:nn #1, + new~sorting~rule .value_required:n = true , +} + + +\keys_set:nn { csvsim } + { + preprocessed~file = \c_sys_jobname_str _sorted._csv, + csvsorter~command = csvsorter, + csvsorter~configpath = ., + csvsorter~log = csvsorter.log, + csvsorter~token = \c_sys_jobname_str.csvtoken, + } + + +\cs_new_protected_nopar:Npn \__csvsim_processor_csvsorter:nnn #1#2#3 + { + \sys_if_shell_unrestricted:TF + { + \__csvsim_set_temp_filename:n { #1 } + \msg_note:nnxx { csvsimple }{ sort-info }{ #2 }{ \l__csvsim_temp_filename_str } + \cs_if_exist:NF \g__csvsim_iow + { + \iow_new:N \g__csvsim_iow + } + \iow_open:Nn \g__csvsim_iow { \l__csvsim_csvsorter_token_str } + \iow_now:Nn \g__csvsim_iow { \ExplSyntaxOn \msg_error:nn { csvsimple }{ sort-error } \ExplSyntaxOff } + \iow_close:N \g__csvsim_iow + \sys_shell_now:x + { + "\l__csvsim_csvsorter_command_str" \c_space_tl + -c~ "\l__csvsim_csvsorter_configpath_str/\l__csvsim_temp_filename_str" \c_space_tl + -l~ "\l__csvsim_csvsorter_log_str" \c_space_tl + -t~ "\l__csvsim_csvsorter_token_str" \c_space_tl + -i~ "#2" \c_space_tl + -o~ "#3" \c_space_tl + -q~1 + } + \file_input:n { \l__csvsim_csvsorter_token_str } + } + { + \msg_error:nn { csvsimple }{ sort-shell-escape } + } + } + + + +%---- default + +\keys_define:nn { csvsim } + { + % default for reset + default .meta:n = + { + file = unknown.csv, + no~preprocessing, + command = \csvline, + column~names~reset, + head, + column~names~detection, + check~column~count, + head~to~column~names~prefix = , + head~to~column~names = false, + collect~data = false, + column~count = 0, + on~column~count~error =, + range =, + no~filter, + before~filter =, + after~filter =, + before~line =, + after~line =, + late~after~line =, + after~head =, + late~after~head =, + before~reading =, + after~reading =, + before~table =, + after~table =, + table~head =, + table~foot =, + no~table, + separator = comma, + respect~none, + }, + } + +\keys_set:nn { csvsim } + { + default + } diff --git a/csvsimple/csvsimple-l3.tex b/csvsimple/csvsimple-l3.tex new file mode 100644 index 0000000..3377217 --- /dev/null +++ b/csvsimple/csvsimple-l3.tex @@ -0,0 +1,3119 @@ +% \LaTeX-Main\ +% !TeX encoding=UTF-8 +% !TeX spellcheck=en_US +%% The LaTeX package csvsimple - version 2.5.0 (2023/10/16) +%% csvsimple.tex: Manual +%% +%% ------------------------------------------------------------------------------------------- +%% Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm +%% ------------------------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `author-maintained'. +%% +%% This work consists of all files listed in README.md +%% +% \RequirePackage[check-declarations,enable-debug]{expl3} +\documentclass[a4paper,11pt]{ltxdoc} +\usepackage{csvsimple-doc} + +\usepackage{\csvpkgprefix csvsimple-l3} + +\tcbmakedocSubKey{docCsvKey}{csvsim} +\tcbmakedocSubKeys{docCsvKeys}{csvsim} + +\hypersetup{ + pdftitle={Manual for the csvsimple-l3 package}, + pdfauthor={Thomas F. Sturm}, + pdfsubject={csv file processing with LaTeX3}, + pdfkeywords={csv file, comma separated values, key value syntax} +} + +\usepackage{incgraph} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\begin{document} + + +\begin{center} +\begin{tcolorbox}[enhanced,hbox,tikznode,left=8mm,right=8mm,boxrule=0.4pt, + colback=white,colframe=black!50!yellow, + drop lifted shadow=black!50!yellow,arc is angular, + before=\par\vspace*{5mm},after=\par\bigskip] +{\bfseries\LARGE The \texttt{csvsimple-l3} package}\\[3mm] +{\large Manual for version \version\ (\datum)} +\end{tcolorbox} +{\large Thomas F.~Sturm% + \footnote{Prof.~Dr.~Dr.~Thomas F.~Sturm, Institut f\"{u}r Mathematik und Informatik, + University of the Bundeswehr Munich, D-85577 Neubiberg, Germany; + email: \href{mailto:thomas.sturm@unibw.de}{thomas.sturm@unibw.de}}\par\medskip +\normalsize\url{https://www.ctan.org/pkg/csvsimple}\par +\url{https://github.com/T-F-S/csvsimple} +} +\end{center} +\bigskip +\begin{absquote} + \begin{center}\bfseries Abstract\end{center} + |csvsimple(-l3)| provides a simple \LaTeX\ interface for the processing of files with + comma separated values (CSV). |csvsimple-l3| relies heavily on the key value + syntax from |l3keys| which results in an easy way of usage. + Filtering and table generation is especially supported. Since the package + is considered as a lightweight tool, there is no support for data sorting + or data base storage. +\end{absquote} + +\vspace{1cm} + +\includegraphics[width=\linewidth]{csvsimple-title.png} +% Source code for the title picture - omitted for PDF viewer compatibility +\begin{tcolorbox}[void] +\begin{NoHyper} +\begin{inctext}[] +\begin{tikzpicture} +\fill[top color=blue!50!gray!50,bottom color=red!50!gray!50] (-8,-5) rectangle (8,5); +\node at (0,2.5) {\tcbinputlisting{listing file=csvsimple-example.csv,listing only,width=11cm,blankest,colupper=blue!50!black}}; +\node[red!50!black] at (0,-2.5) {\csvautotabular{csvsimple-example.csv}}; +\begin{scope}[transparency group=knockout] +\fill [top color=blue!50!gray!10,bottom color=red!50!gray!10] (-7.7,-4.7) rectangle (7.7,4.7); +\node at (0,2.5) {\tcbinputlisting{listing file=csvsimple-example.csv,listing only,width=11cm,blankest,colupper=blue!20}}; +\node[red!20] at (0,-2.5) {\csvautotabular{csvsimple-example.csv}}; +\node at (0,2.5) [opacity=0,font=\fontencoding{T1}\fontfamily{lmr}\fontsize{7cm}{7cm}\bfseries] {csv}; +\node at (0,-2.5) [opacity=0,font=\fontencoding{T1}\fontfamily{lmr}\fontsize{4.8cm}{4.8cm}\bfseries] {simple}; +\end{scope} +\end{tikzpicture} +\end{inctext} +\end{NoHyper} +\end{tcolorbox} + + +\clearpage +\tableofcontents + +\clearpage +\section{Introduction}% +The |csvsimple-l3| package is applied to the processing of +CSV\footnote{CSV file: file with comma separated values.} files. +This processing is controlled by key value assignments according to the +syntax of |l3keys|. Sample applications of the package +are tabular lists, serial letters, and charts. + +An alternative to |csvsimple-l3| is the \ctanpkg{datatool} package +which provides considerably more functions and allows sorting of data by \LaTeX. +|csvsimple-l3| has a different approach for the user interface and +is deliberately restricted to some basic functions with fast +processing speed. + +Mind the following restrictions: +\begin{itemize} +\item Sorting is not supported directly but can be done + with external tools, see \Fullref{sec:Sorting}. +\item Values are expected to be comma separated, but the package + provides support for other separators, see \Fullref{sec:separators}. +\item Values are expected to be either not quoted or quoted with + curly braces |{}| of \TeX\ groups. Other quotes like double-quotes + are not supported directly, but can be achieved + with external tools, see \Fullref{sec:importeddata}. + For approximate patching see \Fullref{sec:hooks}. +\item Every data line is expected to contain the same amount of values. + Unfeasible data lines are silently ignored by default, but this can + be configured, see \Fullref{sec:consistency}. +\end{itemize} + + +\subsection{Loading the Package} +|csvsimple-l3| is loaded with \emph{one} of the following +alternatives inside the preamble: +\begin{dispListing} +\usepackage[l3]{csvsimple} + % or alternatively (not simultaneously!) +\usepackage{csvsimple-l3} +\end{dispListing} + +Not automatically loaded, but used for many examples are the packages +\ctanpkg{longtable}, \ctanpkg{booktabs}, \ctanpkg{ifthen}, and \ctanpkg{etoolbox}. + + +\clearpage +\subsection{First Steps} +Every line of a processable CSV file has to contain an identical amount of +comma\footnote{See \refKey{/csvsim/separator} for other separators than comma.} separated values. The curly braces |{}| of \TeX\ groups can be used +to mask a block which may contain commas not to be processed as separators. + +The first line of such a CSV file is usually but not necessarily a header line +which contains the identifiers for each column. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{grade.csv} +name,givenname,matriculation,gender,grade +Maier,Hans,12345,m,1.0 +Huber,Anna,23456,f,2.3 +Weißbäck,Werner,34567,m,5.0 +Bauer,Maria,19202,f,3.3 +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{grade} + +\smallskip +The most simple way to display a CSV file in tabular form is the processing +with the \refCom{csvautotabular} command. + +\begin{dispExample} +\csvautotabular{grade.csv} +\end{dispExample} + + +Typically, one would use \refCom{csvreader} instead of |\csvautotabular| to +gain full control over the interpretation of the included data. + +In the following example, the entries of the header line are automatically +assigned to \TeX\ macros which may be used deliberately. + + +\begin{dispExample} +\begin{tabular}{|l|c|}\hline% +\bfseries Person & \bfseries Matr.~No. +\csvreader[ + head to column names + ]{grade.csv}{}{% + \\\givenname\ \name & \matriculation + }% +\\\hline +\end{tabular} +\end{dispExample} + + +\clearpage +|\csvreader| is controlled by a plenty of options. For example, for table +applications line breaks are easily inserted by +\refKey{/csvsim/late after line}. This defines a macro execution just before +the following line. +Additionally, the assignment of columns to \TeX\ macros is shown in a non automated +way. + +\begin{dispExample} +\begin{tabular}{|r|l|c|}\hline% +& Person & Matr.~No.\\\hline\hline +\csvreader[ + late after line = \\\hline + ]{grade.csv}% + {name=\name, givenname=\firstname, matriculation=\matnumber}{% + \thecsvrow & \firstname~\name & \matnumber + }% +\end{tabular} +\end{dispExample} + +\smallskip +An even more comfortable and preferable way to create a table is setting +appropriate option keys. Note, that this gives you the possibility to create a +meta key (called style here) which contains the whole table creation +using \refCom{csvstyle} or |keys_define:nn| from |l3keys|. + +\begin{dispExample} +\csvreader[ + tabular = |r|l|c|, + table head = \hline & Person & Matr.~No.\\\hline\hline, + late after line = \\\hline + ]{grade.csv} + {name=\name, givenname=\firstname, matriculation=\matnumber}{% + \thecsvrow & \firstname~\name & \matnumber + }% +\end{dispExample} + + +\clearpage +The next example shows such a style definition with the convenience macro +\refCom{csvstyle}. Here, we see again the automated assignment of header +entries to column names by \refKey{/csvsim/head to column names}. +For this, the header entries have to be without spaces and special characters. +But you can always assign entries to canonical macro names manually like in the examples +above. Here, we also add a \refKey{/csvsim/head to column names prefix} to avoid +macro name clashes. + +\begin{dispExample} +\csvstyle{myTableStyle}{ + tabular = |r|l|c|, + table head = \hline & Person & Matr.~No.\\\hline\hline, + late after line = \\\hline, + head to column names, + head to column names prefix = MY, + } + +\csvreader[myTableStyle] + {grade.csv}{}{% + \thecsvrow & \MYgivenname~\MYname & \MYmatriculation + } +\end{dispExample} + + +\smallskip +Another way to address columns is to use their roman numbers. +The direct addressing is done by |\csvcoli|, |\csvcolii|, |\csvcoliii|, \ldots: + +\begin{dispExample} +\csvreader[ + tabular = |r|l|c|, + table head = \hline & Person & Matr.~No.\\\hline\hline, + late after line = \\\hline + ]{grade.csv}{}{% + \thecsvrow & \csvcolii~\csvcoli & \csvcoliii + } +\end{dispExample} + +\smallskip +And yet another method to assign macros to columns is to use arabic numbers +for the assignment: + +\begin{dispExample} +\csvreader[ + tabular = |r|l|c|, + table head = \hline & Person & Matr.~No.\\\hline\hline, + late after line = \\\hline]% + {grade.csv} + {1=\name, 2=\firstname, 3=\matnumber}{% + \thecsvrow & \firstname~\name & \matnumber + } +\end{dispExample} + +\smallskip +For recurring applications, the |l3keys| syntax allows to create own meta options +(styles) for a consistent and centralized design. The following example is easily +modified to obtain more or less option settings. + +\begin{dispExample} +\csvstyle{myStudentList}{% + tabular = |r|l|c|, + table head = \hline & Person & #1\\\hline\hline, + late after line = \\\hline, + column names = {name=\name, givenname=\firstname} + } + +\csvreader[ myStudentList={Matr.~No.} ] + {grade.csv} + {matriculation=\matnumber}{% + \thecsvrow & \firstname~\name & \matnumber + }% +\hfill% +\csvreader[ myStudentList={Grade} ] + {grade.csv} + {grade=\grade}{% + \thecsvrow & \firstname~\name & \grade + } +\end{dispExample} + + +\clearpage +Alternatively, column names can be set by \refCom{csvnames} +and style definitions by \refCom{csvstyle}. +With this, the last example is rewritten as follows: + +\begin{dispExample} +\csvnames{myNames}{1=\name,2=\firstname,3=\matnumber,5=\grade} +\csvstyle{myStudentList}{ + tabular = |r|l|c|, + table head = \hline & Person & #1\\\hline\hline, + late after line = \\\hline, + myNames + } + +\csvreader[ myStudentList={Matr.~No.} ] + {grade.csv}{}{% + \thecsvrow & \firstname~\name & \matnumber + }% +\hfill% +\csvreader[ myStudentList={Grade} ] + {grade.csv}{}{% + \thecsvrow & \firstname~\name & \grade + } +\end{dispExample} + +\smallskip +The data lines of a CSV file can also be filtered. In the following example, +a certificate is printed only for students with grade unequal to 5.0. + +\begin{dispExample} +\csvreader[ + filter not strcmp={\grade}{5.0} + ]{grade.csv} + {1=\name,2=\firstname,3=\matnumber,4=\gender,5=\grade}{% + \begin{center}\Large\bfseries Certificate in Mathematics\end{center} + \large\ifcsvstrcmp{\gender}{f}{Ms.}{Mr.} + \firstname~\name, matriculation number \matnumber, has passed the test + in mathematics with grade \grade.\par\ldots\par + }% +\end{dispExample} + + +\clearpage +\section{Macros for the Processing of CSV Files}\label{sec:makros}% + +\begin{docCommand}{csvreader}{\oarg{options}\marg{file name}\marg{assignments}\marg{command list}} + \refCom{csvreader} reads the file denoted by \meta{file name} line by line. + Every line of the file has to contain an identical amount of + comma separated values. The curly braces |{}| of \TeX\ groups can be used + to mask a block which may contain commas not to be processed as separators.\smallskip + + The first line of such a CSV file is by default but not necessarily + processed as a header line which contains the identifiers for each column. + The entries of this line can be used to give \meta{assignments} to \TeX\ macros + to address the columns. The number of entries of this first line + determines the accepted number of entries for all following lines. + Every line which contains a higher or lower number of entries is ignored + during standard processing.\smallskip + + The \meta{assignments} are given as comma separated list of key value pairs + \mbox{\meta{name}|=|\meta{macro}}. Here, \meta{name} is an entry from the + header line \emph{or} the arabic number of the addressed column. + \meta{macro} is some \TeX\ macro which gets the content of the addressed column.\smallskip + + The \meta{command list} is executed for every accepted data line. Inside the + \meta{command list} is applicable: + \begin{itemize} + \item \docAuxCommand{thecsvrow} or the counter |csvrow| which contains the number of the + current data line (starting with 1). + \item \docAuxCommand{csvcoli}, \docAuxCommand{csvcolii}, \docAuxCommand{csvcoliii}, \ldots, + which contain the contents of the column entries of the current data line. + Alternatively can be used: + \item \meta{macro} from the \meta{assignments} to have a logical + addressing of a column entry. + \end{itemize} + Note, that the \meta{command list} is allowed to contain |\par| and + that \textbf{all macro definitions are made global} to be used for table applications.\smallskip + + The processing of the given CSV file can be controlled by various + \meta{options} given as key value list. The feasible option keys + are described in section \ref{sec:schluessel} from page \pageref{sec:schluessel}. + +\begin{dispExample} +\csvreader[ + tabular = |r|l|l|, + table head = \hline, + table foot = \hline + ]{grade.csv}% + {name=\name, givenname=\firstname, grade=\grade}{% + \grade & \firstname~\name & \csvcoliii + } +\end{dispExample} + +Mainly, the |\csvreader| command consists of a \refCom{csvloop} macro with +following parameters:\par +|\csvloop{|\meta{options}|, file=|\meta{file name}|, column names=|\meta{assignments}|,|\\ + \hspace*{2cm} |command=|\meta{command list}|}|\par + Therefore, the application of the keys \refKey{/csvsim/file} and \refKey{/csvsim/command} +is useless for |\csvreader|. +\end{docCommand} + + +\clearpage +\begin{docCommand}{csvloop}{\marg{options}} + Usually, \refCom{csvreader} may be preferred instead of |\csvloop|. + \refCom{csvreader} is based on |\csvloop| which takes a mandatory list of + \meta{options} in key value syntax. + This list of \meta{options} controls the total processing. Especially, + it has to contain the CSV file name. +\begin{dispExample} +\csvloop{ + file = {grade.csv}, + head to column names, + command = \name, + before reading = {List of students:\ }, + late after line = {{,}\ }, + late after last line = . + } +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}[doc updated = 2021-06-25]{csvset}{\marg{options}} + Sets \meta{options} for every following + \refCom{csvreader} and \refCom{csvloop}. + Note that most options are set to default values at the begin of these + commands and therefore cannot be defined reasonable by \refCom{csvset}. + But it may be used for options like \refKey{/csvsim/csvsorter command} + to give global settings. Also see \refKey{/csvsim/every csv}. +\end{docCommand} + + +\begin{docCommand}{csvstyle}{\marg{key}\marg{options}} + Defines a new |l3keys| meta key to call other keys. It is used to + make abbreviations for convenient key set applications. + The new \meta{key} can take one parameter. The name \refCom{csvstyle} + originates from an old version of |csvsimple| which used |pgfkeys| + instead of |l3keys|. + +\begin{dispExample} +\csvstyle{grade list}{ + column names = {name=\name, givenname=\firstname, grade=\grade} + } +\csvstyle{passed}{ + filter not strcmp = {\grade}{5.0} + } +The following students passed the test in mathematics:\\ +\csvreader[grade list,passed]{grade.csv}{}{ + \firstname\ \name\ (\grade); + } +\end{dispExample} +\end{docCommand} + +\enlargethispage*{1cm} + +\begin{docCommand}{csvnames}{\marg{key}\marg{assignments}} + Abbreviation for |\csvstyle{|\meta{key}|}{column names=|\marg{assignments}|}| + to define additional \meta{assignments} of macros to columns. +\begin{dispExample} +\csvnames{grade list}{ + name=\name, givenname=\firstname, grade=\grade + } +\csvstyle{passed}{ + filter not strcmp = {\grade}{5.0} + } +The following students passed the test in mathematics:\\ +\csvreader[grade list,passed]{grade.csv}{}{ + \firstname\ \name\ (\grade); + } +\end{dispExample} +\end{docCommand} + + +%\begin{docCommand}{csvheadset}{\marg{assignments}} +% For some special cases, this command can be used to change the +% \meta{assignments} of macros to columns during execution of +% \refCom{csvreader} and \refCom{csvloop}. +%\begin{dispExample} +%\csvreader{grade.csv}{}% +% { \csvheadset{name=\n} \fbox{\n} +% \csvheadset{givenname=\n} \ldots\ \fbox{\n} }% +%\end{dispExample} +%\end{docCommand} + +\clearpage + + +\begin{docCommand}[doc updated=2021-06-28]{ifcsvoddrow}{\marg{then macros}\marg{else macros}} + Inside the command list of \refCom{csvreader}, the \meta{then macros} + are executed for odd-numbered data lines, and the \meta{else macros} + are executed for even-numbered lines. + \refCom{ifcsvoddrow} is expandable. +\begin{dispExample} +\csvreader[ + head to column names, + tabular = |l|l|l|l|, + table head = \hline\bfseries \# & \bfseries Name & \bfseries Grade\\\hline, + table foot = \hline + ]{grade.csv}{}{% + \ifcsvoddrow{\slshape\thecsvrow & \slshape\name, \givenname & \slshape\grade}% + {\bfseries\thecsvrow & \bfseries\name, \givenname & \bfseries\grade} + } +\end{dispExample} + +The |\ifcsvoddrow| macro may be used for striped tables: + +\begin{dispExample} +% This example needs the xcolor package +\csvreader[ + head to column names, + tabular = rlcc, + table head = \hline\rowcolor{red!50!black}\color{white}\# & \color{white}Person + & \color{white}Matr.~No. & \color{white}Grade, + late after head = \\\hline\rowcolor{yellow!50}, + late after line = \ifcsvoddrow{\\\rowcolor{yellow!50}}{\\\rowcolor{red!25}} + ]{grade.csv}{}{% + \thecsvrow & \givenname~\name & \matriculation & \grade + } +\end{dispExample} + +Alternatively, |\rowcolors| from the |xcolor| package can be used for this +purpose: + +\begin{dispExample} +% This example needs the xcolor package +\csvreader[ + head to column names, + tabular = rlcc, + before table = \rowcolors{2}{red!25}{yellow!50}, + table head = \hline\rowcolor{red!50!black}\color{white}\# & \color{white}Person + & \color{white}Matr.~No. & \color{white}Grade\\\hline + ]{grade.csv}{}{% + \thecsvrow & \givenname~\name & \matriculation & \grade + } +\end{dispExample} + + The deprecated, but still available alias for this command is + \docAuxCommand{csvifoddrow}. +\end{docCommand} + +\clearpage + +\begin{docCommand}[doc updated=2021-06-28]{ifcsvfirstrow}{\marg{then macros}\marg{else macros}} + Inside the command list of \refCom{csvreader}, the \meta{then macros} + are executed for the first data line, and the \meta{else macros} + are executed for all following lines. + \refCom{ifcsvfirstrow} is expandable. +\begin{dispExample} +\csvreader[ + tabbing, + head to column names, + table head = {\hspace*{3cm}\=\kill} + ]{grade.csv}{}{% + \givenname~\name \> (\ifcsvfirstrow{first entry!!}{following entry}) + } +\end{dispExample} + The deprecated, but still available alias for this command is + \docAuxCommand{csviffirstrow}. +\end{docCommand} + +\medskip + + +\begin{docCommand}{csvfilteraccept}{} + All following consistent data lines will be accepted and processed. + This command overwrites all previous filter settings and may be used + inside \refKey{/csvsim/full filter} to implement + an own filtering rule together with |\csvfilterreject|. +\begin{dispExample} +\csvreader[ + autotabular, + full filter = \ifcsvstrcmp{\csvcoliv}{m}{\csvfilteraccept}{\csvfilterreject} + ]{grade.csv}{}{% + \csvlinetotablerow + } +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}{csvfilterreject}{} + All following data lines will be ignored. + This command overwrites all previous filter settings. +\end{docCommand} + + +\begin{docCommand}{csvline}{} + This macro contains the current and unprocessed data line. +\begin{dispExample} +\csvreader[ + no head, + tabbing, + table head = {\textit{line XX:}\=\kill} + ]{grade.csv}{}{% + \textit{line \thecsvrow:} \> \csvline + } +\end{dispExample} +\end{docCommand} + + +\clearpage +\begin{docCommand}[doc updated=2022-01-11]{csvlinetotablerow}{} + Typesets the current processed data line with |&| between the entries. + This macro is \emph{expandable}. +\end{docCommand} + + +\begin{docCommands}{ + { doc name = thecsvrow , doc updated = 2021-06-25 }, + { doc name = g_csvsim_row_int, doc new = 2021-06-25 } + } + Typesets the current data line number. This is the + current number of accepted data lines without the header line. + Despite of the name, there is no associated \LaTeX\ counter |csvrow|, + but \refCom{thecsvrow} accesses the \LaTeX3 integer + \refCom{g_csvsim_row_int}. +\end{docCommands} + + +\begin{docCommands}[doc new=2021-06-25]{ + { doc name = thecsvcolumncount }, + { doc name = g_csvsim_columncount_int } + } + Typesets the number of columns of the current CSV file. This number + is either computed from the first valid line (header or data) or + given by \refKey{/csvsim/column count}. + Despite of the name, there is no associated \LaTeX\ counter |csvcolumncount|, + but \refCom{thecsvcolumncount} accesses the \LaTeX3 integer + \refCom{g_csvsim_columncount_int}. +\begin{dispExample} +\csvreader{grade.csv}{}{}% +The last file consists of \thecsvcolumncount{} columns and +\thecsvrow{} accepted data lines. The total number of lines +is \thecsvinputline{}. +\end{dispExample} +\end{docCommands} + + +\begin{docCommands}{ + { doc name = thecsvinputline , doc updated = 2021-06-25 }, + { doc name = g_csvsim_inputline_int, doc new = 2021-06-25 } + } + Typesets the current file line number. This is the + current number of all data lines including the header line and all + lines filtered out. + Despite of the name, there is no associated \LaTeX\ counter |csvinputline|, + but \refCom{thecsvinputline} accesses the \LaTeX3 integer + \refCom{g_csvsim_inputline_int}. +\begin{dispExample} +\csvreader[ + no head, + filter test = \ifnumequal{\thecsvinputline}{3} + ]{grade.csv}{}{% + The line with number \thecsvinputline\ contains: \csvline + } +\end{dispExample} +\end{docCommands} + + + +\clearpage +\section{Macros for Automatic Survey Tables}\label{sec:autotable}% + +The following |\csvauto...| commands are intended for quick data overview +with \emph{limited} formatting potential. +The most customizable |\csvauto...| commands are +\refCom{csvautotabularray} and friends. + +For full control see Subsection~\ref{subsec:tabsupport} on page \pageref{subsec:tabsupport} +for the general table options in combination with \refCom{csvreader} and +\refCom{csvloop}. + +\begin{docCommands}[ + doc parameter = \oarg{options}\marg{file name} + ] + { + { doc name = csvautotabular }, + { doc name = csvautotabular*, doc new = 2021-06-25 } + } + |\csvautotabular| or |\csvautotabular*| + is an abbreviation for the application of the option key + \refKey{/csvsim/autotabular} or \refKey{/csvsim/autotabular*} + together with other \meta{options} to \refCom{csvloop}. + This macro reads the whole CSV file denoted by \meta{file name} + with an automated formatting. + The star variant treats the first line as data line and not as header line. +\begin{dispExample} +\csvautotabular*{grade.csv} +\end{dispExample} +\begin{dispExample} +\csvautotabular[filter equal={\csvcoliv}{f}]{grade.csv} +\end{dispExample} +\end{docCommands} + + + +\begin{docCommands}[ + doc parameter = \oarg{options}\marg{file name} + ] + { + { doc name = csvautolongtable }, + { doc name = csvautolongtable*, doc new = 2021-06-25 } + } + |\csvautolongtable| or |\csvautolongtable*| + is an abbreviation for the application of the option key + \refKey{/csvsim/autolongtable} or \refKey{/csvsim/autolongtable*} + together with other \meta{options} to \refCom{csvloop}. + This macro reads the whole CSV file denoted by \meta{file name} + with an automated formatting. + For application, the package \ctanpkg{longtable} is required which has to be + loaded in the preamble. + The star variant treats the first line as data line and not as header line. +\begin{dispListing} +\csvautolongtable{grade.csv} +\end{dispListing} +\csvautolongtable{grade.csv} +\end{docCommands} + + +\clearpage + + +\begin{docCommands}[ + doc parameter = \oarg{options}\marg{file name} + ] + { + { doc name = csvautobooktabular }, + { doc name = csvautobooktabular*, doc new = 2021-06-25 } + } + |\csvautobooktabular| or |\csvautobooktabular*| + is an abbreviation for the application of the option key + \refKey{/csvsim/autobooktabular} or \refKey{/csvsim/autobooktabular*} + together with other \meta{options} to \refCom{csvloop}. + This macro reads the whole CSV file denoted by \meta{file name} + with an automated formatting. + For application, the package \ctanpkg{booktabs} is required which has to be + loaded in the preamble. + The star variant treats the first line as data line and not as header line. +\begin{dispExample} +\csvautobooktabular{grade.csv} +\end{dispExample} +\end{docCommands} + + +\begin{docCommands}[ + doc parameter = \oarg{options}\marg{file name} + ] + { + { doc name = csvautobooklongtable }, + { doc name = csvautobooklongtable*, doc new = 2021-06-25 } + } + |\csvautobooklongtable| or |\csvautobooklongtable*| + is an abbreviation for the application of the option key + \refKey{/csvsim/autobooklongtable} or \refKey{/csvsim/autobooklongtable*} + together with other \meta{options} to \refCom{csvloop}. + This macro reads the whole CSV file denoted by \meta{file name} + with an automated formatting. + For application, the packages \ctanpkg{booktabs} and \ctanpkg{longtable} are required which have to be + loaded in the preamble. + The star variant treats the first line as data line and not as header line. +\begin{dispListing} +\csvautobooklongtable{grade.csv} +\end{dispListing} +\csvautobooklongtable{grade.csv} +\end{docCommands} + + +\clearpage + +\begin{docCommands}[ + doc parameter = \oarg{options}\marg{file name}\oarg{taboptions 1}\oarg{taboptions 2}, + doc new = 2023-10-13 + ] + { + { doc name = csvautotabularray }, + { doc name = csvautotabularray* }, + { doc name = csvautolongtabularray }, + { doc name = csvautolongtabularray* }, + } + These macros are abbreviations for the application of the option keys + \refKey{/csvsim/autotabularray}, \refKey{/csvsim/autotabularray*},\\ + \refKey{/csvsim/autolongtabularray}, or \refKey{/csvsim/autolongtabularray*} + together with other \meta{options} to \refCom{csvloop}. + These macros read the whole CSV file denoted by \meta{file name} + with an automated formatting. + For application, the package \ctanpkg{tabularray} is required which has to be + loaded in the preamble. + \refCom{csvautotabularray} uses the \docAuxEnvironment*{tblr} environment and + \refCom{csvautolongtabularray} uses the \docAuxEnvironment*{longtblr} environment. + The star variants treat the first line as data line and not as header line.\par + Options to the table environments from \ctanpkg{tabularray} may be given + by either setting \refKey{/csvsim/generic table options} or + using \meta{taboptions 1} and \meta{taboptions 2}.\par + The default setting is +\begin{dispListing} +generic table options = + { { + row{1} = {font=\bfseries}, + hline{1,Z} = {0.8pt}, + hline{2} = {0.4pt}, + } } +\end{dispListing} + For the star variants, the default setting is +\begin{dispListing} +generic table options = + { { + hline{1,Z} = {0.8pt}, + } } +\end{dispListing} + +Examples: + +\begin{dispExample} +\csvautotabularray{grade.csv} +\end{dispExample} + +\begin{dispExample} +\csvautotabularray[table centered, + generic table options = + {{ + row{odd} = {red!85!gray!7}, + row{1} = {bg=red!85!gray, fg=white, font=\bfseries}, + }} + ] + {grade.csv} +\end{dispExample} + +\clearpage + +Alternatively to \refKey{/csvsim/generic table options} +(and overruling this option), one may give options to +\docAuxEnvironment*{tblr} or \docAuxEnvironment*{longtblr} +using \meta{taboptions 1} and \meta{taboptions 2}. +If \meta{taboptions 2} is \emph{not present}, then \meta{taboptions 1} +is used as mandatory argument. +Otherwise, \meta{taboptions 1} is used as optional argument and +\meta{taboptions 2} as mandatory argument. + +\begin{dispExample} +\csvautotabularray[table centered] + {grade.csv} + [ + row{odd} = {red!85!gray!7}, + row{1} = {bg=red!85!gray, fg=white, font=\bfseries}, + ] +\end{dispExample} + + +\begin{dispExample} +\csvautotabularray[table centered] + {grade.csv} + [ + tall, + caption = {My table}, + remark{Note} = {My remark}, + ] + [ + row{odd} = {red!85!gray!7}, + row{1} = {bg=red!85!gray, fg=white, font=\bfseries}, + ] +\end{dispExample} + + +\end{docCommands} + + +\clearpage +\section{Option Keys}\label{sec:schluessel}% +For the \meta{options} in \refCom{csvreader} respectively \refCom{csvloop} +the following |l3keys| keys can be applied. The \meta{module} name |/csvsim/| is not +to be used inside these macros. + + +\subsection{Command Definition}%--------%[[ + +\begin{docCsvKey}{before reading}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed before the CSV file is opened. +\end{docCsvKey} + +\begin{docCsvKey}{after head}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after the header line is read. + \refCom{thecsvcolumncount} and header entries are available. +\end{docCsvKey} + +\begin{docCsvKey}{before filter}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after reading and consistency checking + of a data line. It is executed before any filter condition is checked, + see e.g. \refKey{/csvsim/filter ifthen} and + also see \refKey{/csvsim/full filter}. + No additions to the input stream should be given here. + All line entries are available. +\end{docCsvKey} + +\begin{docCsvKey}[][doc new=2021-07-06]{after filter}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed for an accepted line after + \refKey{/csvsim/late after line} and before \refKey{/csvsim/before line}. + All line entries are available. + No additions to the input stream should be given here. \meta{code} may + contain processing of data content to generate new values. +\end{docCsvKey} + +\begin{docCsvKey}{late after head}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after reading and disassembling + of the first accepted data line. + These operations are executed before further processing of this line. + \meta{code} should not refer to any data content, but may be something + like |\\|. +\end{docCsvKey} + +\begin{docCsvKey}{late after line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after reading and disassembling + of the next accepted data line (after \refKey{/csvsim/before filter}). + These operations are executed before further processing of this line. + \meta{code} should not refer to any data content, but may be something + like |\\|. + \refKey{/csvsim/late after line} overwrites + \refKey{/csvsim/late after first line} and + \refKey{/csvsim/late after last line}. + Note that table options like \refKey{/csvsim/tabular} set this key to |\\| + automatically. +\end{docCsvKey} + + +\begin{docCsvKey}{late after first line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after reading and disassembling + of the second accepted data line instead of \refKey{/csvsim/late after line}. + \meta{code} should not refer to any data content. + This key has to be set after \refKey{/csvsim/late after line}. +\end{docCsvKey} + + +\begin{docCsvKey}{late after last line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after processing of the last + accepted data line instead of \refKey{/csvsim/late after line}. + \meta{code} should not refer to any data content. + This key has to be set after \refKey{/csvsim/late after line}. +\end{docCsvKey} + + +\begin{docCsvKey}{before line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after \refKey{/csvsim/after filter} + and before \refKey{/csvsim/command}. + All line entries are available. + \refKey{/csvsim/before line} overwrites + \refKey{/csvsim/before first line}. +\end{docCsvKey} + + +\begin{docCsvKey}{before first line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed instead of \refKey{/csvsim/before line} + for the first accepted data line. + All line entries are available. + This key has to be set after \refKey{/csvsim/before line}. +\end{docCsvKey} + +\pagebreak + +\begin{docCsvKey}{command}{=\meta{code}}{no default, initially \cs{csvline}} + Sets the \meta{code} to be executed for every accepted data line. + It is executed between \refKey{/csvsim/before line} and \refKey{/csvsim/after line}. + \refKey{/csvsim/command} describes the main processing of the line + entries. \refCom{csvreader} sets \refKey{/csvsim/command} as mandatory + parameter. +\end{docCsvKey} + +\begin{docCsvKey}{after line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed for every accepted data line + after \refKey{/csvsim/command}. + All line entries are still available. + \refKey{/csvsim/after line} overwrites \refKey{/csvsim/after first line}. +\end{docCsvKey} + + +\begin{docCsvKey}{after first line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed instead of \refKey{/csvsim/after line} + for the first accepted data line. + All line entries are still available. + This key has to be set after \refKey{/csvsim/after line}. +\end{docCsvKey} + +\begin{docCsvKey}{after reading}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after the CSV file is closed. +\end{docCsvKey} + +\bigskip + +The following example illustrates the sequence of command execution. +Note that \refKey{/csvsim/command} is set by the mandatory last +parameter of \refCom{csvreader}. + +\begin{dispExample} +\csvreader[ + before reading = \meta{before reading}\\, + after head = \meta{after head}, + before filter = \\\meta{before filter}, + after filter = \meta{after filter}, + late after head = \meta{late after head}, + late after line = \meta{late after line}, + late after first line = \meta{late after first line}, + late after last line = \\\meta{late after last line}, + before line = \meta{before line}, + before first line = \meta{before first line}, + after line = \meta{after line}, + after first line = \meta{after first line}, + after reading = \\\meta{after reading} + ]{grade.csv}{name=\name}{\textbf{\name}}% +\end{dispExample} + +Additional command definition keys are provided for the supported tables, +see Section~\ref{subsec:tabsupport} from page~\pageref{subsec:tabsupport}. + +\clearpage +\subsection{Header Processing and Column Name Assignment}% + +\begin{docCsvKey}{head}{\colOpt{=true\textbar false}}{default |true|, initially |true|} + If this key is set, the first line of the CSV file is treated as a header + line which can be used for column name assignments. +\end{docCsvKey} + + +\begin{docCsvKey}{no head}{}{no value} + Abbreviation for |head=false|, i.\,e. the first line of the CSV file is + treated as data line. + Note that this option cannot be used in combination with + the |\csvauto...| commands like \refCom{csvautotabular}, etc. + Instead, there are \emph{star} variants like \refCom{csvautotabular*} to + process files without header line. + See Section~\ref{noheader} on page~\pageref{noheader} for examples. +\end{docCsvKey} + +\begin{docCsvKey}{column names}{=\marg{assignments}}{no default, initially empty} + Adds some new \meta{assignments} of macros to columns in key value syntax. + Existing assignments are kept.\par + The \meta{assignments} are given as comma separated list of key value pairs + \mbox{\meta{name}|=|\meta{macro}}. Here, \meta{name} is an entry from the + header line \emph{or} the arabic number of the addressed column. + \meta{macro} is some \TeX\ macro which gets the content of the addressed column. +\begin{dispListing} + column names = {name=\surname, givenname=\firstname, grade=\grade} +\end{dispListing} +\end{docCsvKey} + + +\begin{docCsvKey}{column names reset}{}{no value} + Clears all assignments of macros to columns. +\end{docCsvKey} + + +\begin{docCsvKey}{head to column names}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, the entries of the header line are used automatically + as macro names for the columns. This option can be used only, if + the header entries do not contain spaces and special characters to be + used as feasible \LaTeX\ macro names. + Note that the macro definition is \emph{global} and may therefore override + existing macros for the rest of the document. Adding + \refKey{/csvsim/head to column names prefix} may help to avoid unwanted + overrides. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2019-07-16]{head to column names prefix}{=\meta{text}}{no default, initially empty} + The given \meta{text} is prefixed to the name of all macros generated by + \refKey{/csvsim/head to column names}. For example, if you use the settings +\begin{dispListing} + head to column names, + head to column names prefix=MY, +\end{dispListing} + a header entry |section| will generate the corresponding macro + |\MYsection| instead of destroying the standard \LaTeX\ |\section| macro. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2022-02-01]{column names detection}{\colOpt{=true\textbar false}}{default |true|, initially |true|} + If this key is set, the header line is detected for names which can be used + for \refKey{/csvsim/column names} and \refKey{/csvsim/head to column names}. + Otherwise, these options are not functional.\\ + This key can and should be set to |false|, if the header line contains + macros or characters not allowed inside \LaTeX\ control sequences, because + otherwise compilation error are to be expected. +\end{docCsvKey} + + +\clearpage +\subsection{Consistency Check}\label{sec:consistency}% + +\begin{docCsvKey}{check column count}{\colOpt{=true\textbar false}}{default |true|, initially |true|} + This key defines, whether the number of entries in a data line is checked against + an expected value or not.\\ + If |true|, every non consistent line is ignored without announcement.\\ + If |false|, every line is accepted and may produce an error during + further processing. +\end{docCsvKey} + + +\begin{docCsvKey}{no check column count}{}{no value} + Abbreviation for |check column count=false|. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc updated=2021-06-24]{column count}{=\meta{number}}{no default, initially |0|} + Sets the \meta{number} of feasible entries per data line. + If \refKey{/csvsim/column count} is set to |0|, the number of entries of + the first non-empty line determines the column count (automatic detection). + + This setting is only useful in connection with \refKey{/csvsim/no head}, + since \meta{number} would be replaced by the number of entries in the + header line otherwise. +\end{docCsvKey} + + +\begin{docCsvKey}{on column count error}{=\meta{code}}{no default, initially empty} + \meta{code} to be executed for unfeasible data lines. +\end{docCsvKey} + + +\begin{docCsvKey}{warn on column count error}{}{style, no value} + Display of a warning for unfeasible data lines. +\end{docCsvKey} + + +\clearpage +\subsection{Filtering}\label{subsec:filtering}% + +Applying a \emph{filter} means that data lines are only processed / displayed, +if they fulfill a given \emph{condition}. + +The following string compare filters \refKey{/csvsim/filter strcmp} and +\refKey{/csvsim/filter equal} are identical by logic, but differ in implementation. + +\begin{docCsvKey}[][doc updated=2022-10-21]{filter strcmp}{=\marg{stringA}\marg{stringB}}{no default} + Only lines where \meta{stringA} and \meta{stringB} are equal after expansion + are accepted. + The implementation is done with \docAuxCommand*{str_if_eq_p:ee}. +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + filter strcmp = {\gender}{f}, %>> list only female persons << + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCsvKey} + + +\begin{docCsvKey}[][doc updated=2022-10-21]{filter not strcmp}{=\marg{stringA}\marg{stringB}}{no default} + Only lines where \meta{stringA} and \meta{stringB} are not equal after expansion + are accepted. + The implementation is done with \docAuxCommand*{str_if_eq_p:ee}. +\end{docCsvKey} + + +\begin{docCsvKey}{filter equal}{=\marg{stringA}\marg{stringB}}{no default} + Only lines where \meta{stringA} and \meta{stringB} are equal after expansion + are accepted. + The implementation is done with the \ctanpkg{ifthen} package (loading required!). +\end{docCsvKey} + + +\begin{docCsvKey}{filter not equal}{=\marg{stringA}\marg{stringB}}{no default} + Only lines where \meta{stringA} and \meta{stringB} are not equal after expansion + are accepted. + The implementation is done with the \ctanpkg{ifthen} package (loading required!). +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new and updated={2021-06-25}{2022-10-21}]{filter fp}{=\marg{floating point expression}}{no default} + Only data lines which fulfill a \LaTeX3 \meta{floating point expression} + (|l3fp|, \ctanpkg{xfp}) are accepted. +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + %>> list only matriculation numbers greater than 20000 + % and grade less than 4.0 << + filter fp = { \matriculation > 20000 && \grade < 4.0 }, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCsvKey} + + +\clearpage + +\begin{docCsvKey}[][doc new and updated={2021-06-25}{2022-10-21}]{filter bool}{=\marg{boolean expression}}{no default} + Only data lines which fulfill a \LaTeX3 \meta{boolean expression} are accepted. + Note that such an \meta{boolean expression} needs expl3 code. + To preprocess the data line before testing the \meta{boolean expression}, + the option key \refKey{/csvsim/before filter} can be used. +\begin{dispExample} +% For convenience, we save the filter +\ExplSyntaxOn +%>> list only matriculation numbers greater than 20000, list only men << +\csvstyle{myfilter} + { + filter~bool = + { + \int_compare_p:n { \matriculation > 20000 } && + \str_if_eq_p:ee { \gender }{ m } + } + } +\ExplSyntaxOff + +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + myfilter + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCsvKey} + +\medskip +\begin{docCommand}[doc new=2021-06-25]{csvfilterbool}{\marg{key}\marg{boolean expression}} + Defines a new |l3keys| meta key which applies \refKey{/csvsim/filter bool} + with the given \meta{boolean expression}. +\begin{dispExample} +% For convenience, we save the filter +\ExplSyntaxOn +%>> list only matriculation numbers greater than 20000, list only men << +\csvfilterbool{myfilter} + { + \int_compare_p:n { \matriculation > 20000 } && + \str_if_eq_p:ee { \gender }{ m } + } +\ExplSyntaxOff + +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + myfilter + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCommand} + + +\clearpage + +The following filter options are \emph{appendable} to the expl3 based +filter options: +\begin{itemize} +\item \refKey{/csvsim/filter strcmp} +\item \refKey{/csvsim/filter not strcmp} +\item \refKey{/csvsim/filter fp} +\item \refKey{/csvsim/filter bool} +\end{itemize} + +\begin{docCsvKeys}[ + doc parameter = {=\marg{stringA}\marg{stringB}}, + doc description = {no default}, + doc new = {2022-10-21} + ] + { + { doc name = and filter strcmp }, + { doc name = or filter strcmp }, + } + Like \refKey{/csvsim/filter strcmp}, but appended to a required existing + expl3 based filter with \emph{and} (|&&|) resp. \emph{or} (\texttt{\textbar\textbar}). + +\begin{dispExample} +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + filter fp = {\matriculation>20000}, + and filter strcmp = {\gender}{m}, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCsvKeys} + + +\begin{docCsvKeys}[ + doc parameter = {=\marg{stringA}\marg{stringB}}, + doc description = {no default}, + doc new = {2022-10-21} + ] + { + { doc name = and filter not strcmp }, + { doc name = or filter not strcmp }, + } + Like \refKey{/csvsim/filter not strcmp}, but appended to a required existing + expl3 based filter with \emph{and} (|&&|) resp. \emph{or} (\texttt{\textbar\textbar}). +\end{docCsvKeys} + + +\begin{docCsvKeys}[ + doc parameter = {=\marg{floating point expression}}, + doc description = {style, no default}, + doc new = {2022-10-21} + ] + { + { doc name = and filter fp }, + { doc name = or filter fp }, + } + Like \refKey{/csvsim/filter fp}, but appended to a required existing + expl3 based filter with \emph{and} (|&&|) resp. \emph{or} (\texttt{\textbar\textbar}). +\end{docCsvKeys} + + +\begin{docCsvKeys}[ + doc parameter = {=\marg{boolean expression}}, + doc description = {style, no default}, + doc new = {2022-10-21} + ] + { + { doc name = and filter bool }, + { doc name = or filter bool }, + } + Like \refKey{/csvsim/filter bool}, but appended to a required existing + expl3 based filter with \emph{and} (|&&|) resp. \emph{or} (\texttt{\textbar\textbar}). +\end{docCsvKeys} + + +\clearpage + +\begin{docCsvKey}[][doc new=2016-07-01]{filter test}{=\meta{condition}}{no default} + Only data lines which fulfill a logical \meta{condition} are accepted. + For the \meta{condition}, every single test normally employed like +\begin{dispListing} +\iftest{some testing}{true}{false} +\end{dispListing} + can be used as +\begin{dispListing} +filter test=\iftest{some testing}, +\end{dispListing} + For |\iftest|, tests from the \ctanpkg{etoolbox} package like + |\ifnumcomp|, |\ifdimgreater|, etc. and from \Fullref{sec:stringtests} can be used. + Also, arbitrary own macros fulfilling this signature can be applied. +\begin{dispExample} +% \usepackage{etoolbox,booktabs} +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + %>> list only matriculation numbers greater than 20000 << + filter test = \ifnumgreater{\matriculation}{20000}, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCsvKey} + + +\medskip +\begin{docCsvKey}[][doc new=2016-07-01]{filter expr}{=\meta{boolean expression}}{no default} + Only data lines which fulfill a \meta{boolean expression} are accepted. + Every \meta{boolean expression} + from the \ctanpkg{etoolbox} package is feasible (package loading required!). + To preprocess the data line before testing the \meta{boolean expression}, + the option key \refKey{/csvsim/before filter} can be used. +\begin{dispExample} +% \usepackage{etoolbox,booktabs} +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + %>> list only matriculation numbers greater than 20000 + % and grade less than 4.0 << + filter expr = { test{\ifnumgreater{\matriculation}{20000}} + and test{\ifdimless{\grade pt}{4.0pt}} }, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCsvKey} + + +\clearpage +\begin{docCsvKey}[][doc new=2016-07-01]{filter ifthen}{=\meta{boolean expression}}{no default} + Only data lines which fulfill a \meta{boolean expression} are accepted. + For the \meta{boolean expression}, every term from the \ctanpkg{ifthen} package + is feasible (package loading required!). + To preprocess the data line before testing the \meta{boolean expression}, + the option key \refKey{/csvsim/before filter} can be used. + +\begin{dispExample} +% \usepackage{ifthen,booktabs} +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + %>> list only female persons << + filter ifthen=\equal{\gender}{f}, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCsvKey} + + +\begin{docCsvKey}{no filter}{}{no value, initially set} + Clears a set filter. +\end{docCsvKey} + + +\begin{docCsvKey}{filter accept all}{}{no value, initially set} + Alias for |no filter|. All consistent data lines are accepted. +\end{docCsvKey} + + +\begin{docCsvKey}{filter reject all}{}{no value} + All data line are ignored. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2016-07-01]{full filter}{=\meta{code}}{no default} + Technically, this key is an alias for \refKey{/csvsim/before filter}. + Philosophically, \refKey{/csvsim/before filter} computes something before + a filter condition is set, but \refKey{/csvsim/full filter} should implement + the full filtering. Especially, \refCom{csvfilteraccept} or + \refCom{csvfilterreject} \emph{should} be set inside the \meta{code}. +\begin{dispExample} +% \usepackage{etoolbox,booktabs} +\csvreader[ + head to column names, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + %>> list only matriculation numbers greater than 20000 + % and grade less than 4.0 << + full filter = \ifnumgreater{\matriculation}{20000} + {\ifdimless{\grade pt}{4.0pt}{\csvfilteraccept}{\csvfilterreject}} + {\csvfilterreject}, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} +\end{docCsvKey} + + + +%]] + +\clearpage +\subsection{Line Range}\label{subsec:linerange} + +Applying a \emph{line range} means to select certain line numbers to be +displayed. These line numbers are not necessarily line numbers of +the input file, see \refCom{thecsvinputline}, but line numbers of +type \refCom{thecsvrow}. + +For example, if a \emph{filter} was applied, see \Fullref{subsec:filtering}, +and 42 lines are accepted, a \emph{range} could select the first 20 of them or +line 10 to 30 of the accepted lines. + + +\begin{docCsvKey}[][doc new and updated={2021-06-29}{2022-09-21}]{range}{=\brackets{\meta{range1},\meta{range2},\meta{range3},... }}{no default, initially empty} + Defines a comma separated list of line ranges. If a line number \refCom{thecsvrow} + satisfies one or more of the given \meta{range1}, \meta{range2}, \ldots, + the corresponding line is processed and displayed. + If \refKey{/csvsim/range} is set to empty, all lines are accepted. + + Every \meta{range} can + corresponds to one of the following variants: + \begin{tabbing} + \hspace*{2cm}\=\kill + \texttt{\meta{a}-\meta{b}} \> meaning line numbers \meta{a} to \meta{b}.\\ + \texttt{\meta{a}-} \> meaning line numbers \meta{a} to |\c_max_int|=2 147 483 647.\\ + \texttt{-\meta{b}} \> meaning line numbers 1 to \meta{b}.\\ + \texttt{-} \> meaning line numbers 1 to 2 147 483 647 (inefficient; don't use).\\ + \texttt{\meta{a}} \> meaning line numbers \meta{a} to \meta{a} (i.e. only \meta{a}).\\ + \texttt{\meta{a}+\meta{d}} \> meaning line numbers \meta{a} to \meta{a}$+$\meta{d}$-1$.\\ + \texttt{\meta{a}+} \> meaning line numbers \meta{a} to \meta{a} (i.e. only \meta{a}).\\ + \texttt{+\meta{d}} \> meaning line numbers 1 to \meta{d}.\\ + \texttt{+} \> meaning line numbers 1 to 1 (i.e. only 1; weird).\\ + \end{tabbing} + +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + range = 2-3, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} + + +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + range = 3-, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} + + +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + range = 2+2, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} + +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + range = {2,4}, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} + +To select the last $n$ lines, you have to know or count the line numbers first. +The following example displays the last three line numbers: + +\begin{dispExample} +% \usepackage{booktabs} +\csvreader{grade.csv}{}{}% count line numbers +\csvreader[ + head to column names, + range = {\thecsvrow-2}-, + tabular = llll, + table head = \toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot = \bottomrule, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade + } +\end{dispExample} + +\end{docCsvKey} + + + +\clearpage +\subsection{Table Support}\label{subsec:tabsupport}%--------%[[ + +\subsubsection{Predefined Tables}\label{subsubsec:table_predef} + +\begin{docCsvKey}{tabular}{=\meta{table format}}{style, no default} + Surrounds the CSV processing with |\begin{tabular}|\marg{table format} + at begin and with |\end{tabular}| at end. + Additionally, the commands defined by the key values of + \refKey{/csvsim/before table}, \refKey{/csvsim/table head}, \refKey{/csvsim/table foot}, + and \refKey{/csvsim/after table} are executed at the appropriate places. + \refKey{/csvsim/late after line} is set to \cs{}\cs{}. +\end{docCsvKey} + + +\begin{docCsvKey}{centered tabular}{=\meta{table format}}{style, no default} + Like \refKey{/csvsim/tabular} but inside an additional |center| environment. +\end{docCsvKey} + + +\begin{docCsvKey}{longtable}{=\meta{table format}}{style, no default} + Like \refKey{/csvsim/tabular} but for the |longtable| environment. + This requires the package \ctanpkg{longtable} (not loaded automatically). +\end{docCsvKey} + + +\begin{docCsvKey}{tabbing}{}{style, no value} + Like \refKey{/csvsim/tabular} but for the |tabbing| environment. +\end{docCsvKey} + + +\begin{docCsvKey}{centered tabbing}{}{style, no value} + Like \refKey{/csvsim/tabbing} but inside an additional |center| environment. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2021-07-06]{tabularray}{=\meta{table format}}{style, no default} + Like \refKey{/csvsim/tabular} but for the |tblr| environment. + This requires the package \ctanpkg{tabularray} (not loaded automatically). + This also sets \refKey{/csvsim/collect data} since this kind of table + needs collected content, see \Fullref{sec:datacollection}. + Note that \refKey{/csvsim/after reading} is set to use the collected + data immediately. See \Fullref{sec:tabularray} for examples. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2021-07-23]{long tabularray}{=\meta{table format}}{style, no default} + Like \refKey{/csvsim/tabularray} but using the |longtblr| environment + from the package \ctanpkg{tabularray} (not loaded automatically). +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2021-07-06]{centered tabularray}{=\meta{table format}}{style, no default} + Like \refKey{/csvsim/tabularray} but inside an additional |center| environment. +\end{docCsvKey} + + +\begin{docCsvKey}{no table}{}{style, no value} + Deactivates |tabular|-like environments activated by + \refKey{/csvsim/tabular}, \refKey{/csvsim/longtable}, etc. + Note that not all settings of \refKey{/csvsim/tabularray} are reverted. +\end{docCsvKey} + + +\clearpage +\subsubsection{Additional Options for Tables}\label{subsubsec:table_options} + +\begin{docCsvKey}{before table}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed before the begin of |tabular|-like environments, + i.e. immediately before |\begin{tabular}|, etc. +\end{docCsvKey} + + +\begin{docCsvKey}{table head}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after the begin of |tabular|-like environments, + i.e. immediately after |\begin{tabular}|, etc. +\end{docCsvKey} + + +\begin{docCsvKey}{table foot}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed before the end of |tabular|-like environments, + i.e. immediately before |\end{tabular}|, etc. +\end{docCsvKey} + + +\begin{docCsvKey}{after table}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after the end of |tabular|-like environments, + i.e. immediately after |\end{tabular}|, etc. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2021-09-09]{table centered}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If |true|, the table is put inside an additional |center| environment. + This environment begins before \refKey{/csvsim/before table} + and ends after \refKey{/csvsim/after table}. The predefined |tabular|-like environments + from Section~\fullref{subsubsec:table_predef} use this option internally, + i.e. \mbox{|centered tabular={ccc}|} is identical to + \mbox{|tabular={ccc}, table centered|}. +\end{docCsvKey} + + +\clearpage +\subsubsection{Generic Tables}\label{subsubsec:table_generic} +In Section~\fullref{subsubsec:table_predef}, several |tabular|-like environments +are described with predefined keys. The following keys allow to use further +|tabular|-like environments with configurable names and options. + + +\begin{docCsvKey}[][doc new=2021-09-09]{generic table}{=\meta{name}}{no default, initially empty} + Surrounds the CSV processing with \cs{begin}\marg{name} + at begin and with \cs{end}\marg{name} at end. + Additionally, the commands defined by the key values of + \refKey{/csvsim/before table}, \refKey{/csvsim/table head}, \refKey{/csvsim/table foot}, + and \refKey{/csvsim/after table} are executed at the appropriate places. + \refKey{/csvsim/late after line} is set to \cs{}\cs{}.\par + If the environment \meta{name} takes options, these have to be set using + \refKey{/csvsim/generic table options}. + +\begin{dispListing} + % The `tabular` environment would be used like the following example + ... + generic table = tabular, + generic table options = {{ccllrr}}, + ... +\end{dispListing} +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2021-09-09]{generic collected table}{=\meta{name}}{no default, initially empty} + Like \refKey{/csvsim/generic table} but for environments which need + collected content, e.g. |tblr| from package \ctanpkg{tabularray}, see \Fullref{sec:datacollection}. + Note that \refKey{/csvsim/after reading} is set to use the collected + data immediately. + +\begin{dispListing} + % The `tblr` environment from package `tabularray` would be used + % like the following example + ... + generic collected table = tblr, + generic table options = {{rowsep=1mm, colsep=5mm}}, + ... +\end{dispListing} +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2021-09-09]{generic table options}{=\marg{code}}{no default, initially empty} + Places \meta{code} immediately after \cs{begin}\marg{name} set up with + \refKey{/csvsim/generic table} or \refKey{/csvsim/generic collected table}. + \meta{code} may contain any parameters the environment \meta{name} needs to have. + \textbf{\color{red!50!black}You are strongly advised to use an extra pair of + curly brackets \marg{code} around \meta{code}}, because the outer pair of braces is + removed during option processing, see examples above. +\begin{dispListing} + % Environment without parameters: + generic table options =, + % Environment with a mandatory parameter: + generic table options = {{parameter}}, + % Environment with an optional and a mandatory parameter: + generic table options = {[optional]{mandatory}}, + % Environment with two mandatory parameters: + generic table options = {{mandatory 1}{mandatory 2}}, +\end{dispListing} + +\end{docCsvKey} + + +\clearpage +\subsubsection{General Survey Tables}\label{subsubsec:table_survey} + +The following |auto| options are the counterparts for the respective quick +overview commands like \refCom{csvautotabular}, see Section~\ref{sec:autotable}. +They are listed for +completeness, but are unlikely to be used directly. + +\begin{docCsvKeys}[ + doc parameter = {=\meta{file name}}, + doc description = no default, + ] + { + { doc name = autotabular, doc updated = {2022-02-01} }, + { doc name = autotabular* }, + } + Reads the whole CSV file denoted \meta{file name} with an automated formatting. + The star variant treats the first line as data line and not as header line. +\end{docCsvKeys} + + +\begin{docCsvKeys}[ + doc parameter = {=\meta{file name}}, + doc description = no default, + ] + { + { doc name = autolongtable, doc updated = {2022-02-01} }, + { doc name = autolongtable* }, + } + Reads the whole CSV file denoted \meta{file name} with an automated formatting + using the required |longtable| package. + The star variant treats the first line as data line and not as header line. +\end{docCsvKeys} + + +\begin{docCsvKeys}[ + doc parameter = {=\meta{file name}}, + doc description = no default, + ] + { + { doc name = autobooktabular, doc updated = {2022-02-01} }, + { doc name = autobooktabular* }, + } + Reads the whole CSV file denoted \meta{file name} with an automated formatting + using the required |booktabs| package. + The star variant treats the first line as data line and not as header line. +\end{docCsvKeys} + + +\begin{docCsvKeys}[ + doc parameter = {=\meta{file name}}, + doc description = no default, + ] + { + { doc name = autobooklongtable, doc updated = {2022-02-01} }, + { doc name = autobooklongtable* }, + } + Reads the whole CSV file denoted \meta{file name} with an automated formatting + using the required |booktabs| and |longtable| packages. + The star variant treats the first line as data line and not as header line. +\end{docCsvKeys} + + +\begin{docCsvKeys}[ + doc parameter = {=\meta{file name}}, + doc description = no default, + doc new = {2023-10-13} + ] + { + { doc name = autotabularray }, + { doc name = autotabularray* }, + { doc name = autolongtabularray }, + { doc name = autolongtabularray* }, + } + Reads the whole CSV file denoted \meta{file name} with an automated formatting + using the required |tabularray| package. + \refKey{/csvsim/autotabularray} uses the \docAuxEnvironment*{tblr} environment and + \refKey{/csvsim/autolongtabularray} uses the \docAuxEnvironment*{longtblr} environment. + The star variants treat the first line as data line and not as header line. +\end{docCsvKeys} + + +\clearpage +\subsection{Special Characters}\label{subsec:specchar} +Be default, the CSV content is treated like normal \LaTeX\ text, see +Subsection~\ref{macrocodexample} on page~\pageref{macrocodexample}. +But, \TeX\ special characters of the CSV content may also be interpreted +as normal characters (|\catcode| 12, other), if one or more of the following options are used. + +\begin{docCsvKey}{respect tab}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + tabulator sign + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect percent}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + percent sign \verbbox{\%} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect sharp}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + sharp sign \verbbox{\#} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect dollar}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + dollar sign \verbbox{\$} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect and}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + and sign \verbbox{\&} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect backslash}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + backslash sign \verbbox{\textbackslash} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect underscore}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + underscore sign \verbbox{\_} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect tilde}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + tilde sign \verbbox{\textasciitilde} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect circumflex}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + circumflex sign \verbbox{\textasciicircum} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect leftbrace}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + left brace sign \verbbox{\textbraceleft} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect rightbrace}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + right brace sign \verbbox{\textbraceright} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect all}{}{style, no value, initially unset} + Set all special characters from above to normal characters. This means + a quite verbatim interpretation of the CSV content. +\end{docCsvKey} + +\begin{docCsvKey}{respect none}{}{style, no value, initially set} + Do not change any special character from above to normal character. +\end{docCsvKey} + +\clearpage +\subsection{Separators}\label{sec:separators}% +\begin{docCsvKey}{separator}{=\meta{sign}}{no default, initially |comma|} + \catcode `|=12 + Sets the \meta{sign} which is treated as separator between the data values + of a data line. Feasible values are: + \begin{itemize} + \item\docValue{comma}: This is the initial value with '\texttt{,}' as separator. + \medskip + + \item\docValue{semicolon}: Sets the separator to '\texttt{;}'. +\begin{dispExample} +% \usepackage{tcolorbox} for tcbverbatimwrite +\begin{tcbverbatimwrite}{testsemi.csv} + name;givenname;matriculation;gender;grade + Maier;Hans;12345;m;1.0 + Huber;Anna;23456;f;2.3 + Weißbäck;Werner;34567;m;5.0 +\end{tcbverbatimwrite} + +\csvautobooktabular[separator=semicolon]{testsemi.csv} +\end{dispExample} +\medskip + +\item\docValue{pipe}: Sets the separator to '\texttt{|}'. +\begin{dispExample} +% \usepackage{tcolorbox} for tcbverbatimwrite +\begin{tcbverbatimwrite}{pipe.csv} + name|givenname|matriculation|gender|grade + Maier|Hans|12345|m|1.0 + Huber|Anna|23456|f|2.3 + Weißbäck|Werner|34567|m|5.0 +\end{tcbverbatimwrite} + +\csvautobooktabular[separator=pipe]{pipe.csv} +\end{dispExample} +\medskip + +\item\docValue{tab}: Sets the separator to the tabulator sign. + Automatically, \refKey{/csvsim/respect tab} is set also. + +\clearpage +\item\docValue{space}:\tcbdocmarginnote{\tcbdocnew{2023-05-08}} + Sets the separator to space(s). +\begin{dispExample} +% \usepackage{tcolorbox} for tcbverbatimwrite +\begin{tcbverbatimwrite}{space.csv} + name givenname matriculation gender grade + Maier Hans 12345 m 1.0 + Huber Anna 23456 f 2.3 + Weißbäck Werner 34567 m 5.0 +\end{tcbverbatimwrite} + +\csvautobooktabular[separator=space]{space.csv} +\end{dispExample} + Note that leading spaces are ignored and multiple spaces are treated as one space. + To denote an empty data cell insert \verb+{}+, e.g. \verb*+1 {} 3+. + \end{itemize} +\end{docCsvKey} + +\clearpage +\subsection{Miscellaneous}% + +\begin{docCsvKey}{every csv}{}{style, initially empty} + A meta key (style) definition which is used for every following CSV file. + This definition can be overwritten with user code. +\begin{dispListing} +% Sets a warning message for unfeasible data lines. +\csvstyle{every csv}{warn on column count error} +\end{dispListing} +\end{docCsvKey} + +\begin{docCsvKey}{default}{}{style} + A style definition which is used for every following CSV file which + resets all settings to default values\footnote{\texttt{default} is used + because of the global nature of most settings.}. + This key should not be used or changed by the user if there is not a + really good reason (and you know what you do). +\end{docCsvKey} + + +\begin{docCsvKey}{file}{=\meta{file name}}{no default, initially |unknown.csv|} + Sets the \meta{file name} of the CSV file to be processed. + \refCom{csvreader} sets this option by a mandatory parameter. +\end{docCsvKey} + + +\begin{docCsvKey}{preprocessed file}{=\meta{file name}}{no default, initially \texttt{\textbackslash\detokenize{jobname_sorted.csv}}} + Sets the \meta{file name} of the CSV file which is the output of a + preprocessor. +\end{docCsvKey} + + +\begin{docCsvKey}{preprocessor}{=\meta{macro}}{no default} + Defines a preprocessor for the given CSV file. + The \meta{macro} has to have two mandatory arguments. The first argument + is the original CSV file which is set by \refKey{/csvsim/file}. + The second argument is the preprocessed CSV file + which is set by \refKey{/csvsim/preprocessed file}.\par\smallskip + Typically, the \meta{macro} may call an external program which preprocesses + the original CSV file (e.\,g. sorting the file) and creates the + preprocessed CSV file. The later file is used by \refCom{csvreader} + or \refCom{csvloop}. +\begin{dispListing} +\newcommand{\mySortTool}[2]{% + % call to an external program to sort file #1 with resulting file #2 +} + +\csvreader[% + preprocessed file = \jobname_sorted.csv, + preprocessor = \mySortTool, + ]{some.csv}{}{% + % do something +} +\end{dispListing} +See Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting} for a +concrete sorting preprocessing implemented with an external tool. +\end{docCsvKey} + + +\begin{docCsvKey}{no preprocessing}{}{style, no value, initially set} + Clears any preprocessing, i.\,e. preprocessing is switched of. +\end{docCsvKey} + + + +\clearpage +\subsection{Sorting}\label{sec:Sorting}% +\TeX/\LaTeX\ was not born under a sorting planet. |csvsimple-l3| provides no +sorting of data lines by \LaTeX-methods since sorting can be done much faster +and much better by external tools. + +First, one should consider the appropriate \emph{place} for sorting: +\begin{itemize} +\item CSV files may be sorted by a tool \emph{before} the \LaTeX\ document is processed + at all. If the CSV data is not likely to change, this is the most efficient method. +\item CSV files may be sorted by a tool every time before the \LaTeX\ document is compiled. + This could be automated by a shell script or some processing tool like |arara|. +\item CSV files may be sorted on-the-fly by a tool during compilation of + a \LaTeX\ document. This is the most elegant but not the most efficient way. +\end{itemize} + +The first two methods are decoupled from anything concerning |csvsimple-l3|. +For the third method, the \refKey{/csvsim/preprocessor} option is made for. +This allows to access an external tool for sorting. +\emph{Which tool} is your choice. + +\csvsorter\ was written as a companion tool for |csvsimple|. +It is an open source Java command-line tool for sorting CSV files, available at\\ +\url{https://T-F-S.github.io/csvsorter/}\quad or\quad +\url{https://github.com/T-F-S/csvsorter} + +It can be +used for all three sorting approaches described above. +There is special support for on-the-fly sorting with \csvsorter\ using the +following options. + +\begin{enumerate}\bfseries +\item To use the sorting options, you have to install \csvsorter\ before! +\item You have to give permission to call external tools during + compilation, i.\,e.\ the command-line options for |latex| have to include + |-shell-escape|. +\end{enumerate} + +\bigskip + +\begin{docCsvKey}{csvsorter command}{=\meta{system command}}{no default, initially |csvsorter|} + The \meta{system command} specifies the system call for \csvsorter\ (without the options). + If \csvsorter\ was completely installed following its documentation, there is + nothing to change here. If the |csvsorter.jar| file is inside the same + directory as the \LaTeX\ source file, you may configure:% preferrably inside the preamble: +\begin{dispListing} +\csvset{csvsorter command=java -jar csvsorter.jar} +\end{dispListing} +\end{docCsvKey} + +\begin{docCsvKey}{csvsorter configpath}{=\meta{path}}{no default, initially |.|} + Sorting with \csvsorter\ is done using XML configuration files. If these files + are not stored inside the same directory as the \LaTeX\ source file, a + \meta{path} to access them can be configured: +\begin{dispListing} +\csvset{csvsorter configpath=xmlfiles} +\end{dispListing} + Here, the configuration files would be stored in a subdirectory named |xmlfiles|. +\end{docCsvKey} + +\begin{docCsvKey}{csvsorter log}{=\meta{file name}}{no default, initially |csvsorter.log|} + Sets the log file of \csvsorter\ to the given \meta{file name}. +\begin{dispListing} +\csvset{csvsorter log=outdir/csvsorter.log} +\end{dispListing} + Here, the log file is written to a subdirectory named |outdir|. +\end{docCsvKey} + +\clearpage +\begin{docCsvKey}{csvsorter token}{=\meta{file name}}{no default, initially |\textbackslash jobname.csvtoken|} + Sets \meta{file name} as token file. This is an auxiliary file which + communicates the success of \csvsorter\ to |csvsimple|. +\begin{dispListing} +\csvset{csvsorter log=outdir/\jobname.csvtoken} +\end{dispListing} + Here, the token file is written to a subdirectory named |outdir|. +\end{docCsvKey} + + +\begin{docCsvKey}{sort by}{=\meta{file name}}{style, initially unset} + The \meta{file name} denotes an XML configuration file for \csvsorter. + Setting this option inside \refCom{csvreader} or + \refCom{csvloop} will issue a system call to \csvsorter. + \begin{itemize} + \item \csvsorter\ uses the given CSV file as input file. + \item \csvsorter\ uses \meta{file name} as configuration file. + \item The output CSV file is denoted by \refKey{/csvsim/preprocessed file} + which is by default \texttt{\textbackslash\detokenize{jobname_sorted.csv}}. + This output file is this actual file processed by \refCom{csvreader} or \refCom{csvloop}. + \item \csvsorter\ also generates a log file denoted by \refKey{/csvsim/csvsorter log} which is by default |csvsorter.log|. + \end{itemize} + +\par\medskip\textbf{First example:} + To sort our example |grade.csv| file according to |name| and |givenname|, we + use the following XML configuration file. Since \csvsorter\ uses double quotes + as default brackets for column values, we remove bracket recognition to avoid + a clash with the escaped umlauts of the example CSV file.\par\smallskip + +\xmllisting{namesort} +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + sort by = namesort.xml, + tabular = >{\color{red}}lllll, + table head = \toprule Name & Given Name & Matriculation & Gender & Grade\\\midrule, + table foot = \bottomrule + ]{grade.csv}{}{% + \csvlinetotablerow + } +\end{dispExample} + +\clearpage\textbf{Second example:} + To sort our example |grade.csv| file according to |grade|, we + use the following XML configuration file. Further, persons with the same |grade| + are sorted by |name| and |givenname|. Since \csvsorter\ uses double quotes + as default brackets for column values, we remove bracket recognition to avoid + a clash with the escaped umlauts of the example CSV file.\par\smallskip + +\xmllisting{gradesort} +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + sort by = gradesort.xml, + tabular = llll>{\color{red}}l, + table head = \toprule Name & Given Name & Matriculation & Gender & Grade\\\midrule, + table foot = \bottomrule + ]{grade.csv}{}{% + \csvlinetotablerow + } +\end{dispExample} + +\clearpage\textbf{Third example:} + To generate a matriculation/grade list, we sort our example |grade.csv| file + using the following XML configuration file. + Again, since \csvsorter\ uses double quotes + as default brackets for column values, we remove bracket recognition to avoid + a clash with the escaped umlauts of the example CSV file.\par\smallskip + +\xmllisting{matriculationsort} +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[ + head to column names, + sort by = matriculationsort.xml, + tabular = >{\color{red}}ll, + table head = \toprule Matriculation & Grade\\\midrule, + table foot = \bottomrule + ]{grade.csv}{}{% + \matriculation & \grade + } +\end{dispExample} +\end{docCsvKey} + + +\clearpage +\begin{docCsvKey}{new sorting rule}{=\marg{name}\marg{file name}}{style, initially unset} +This is a convenience option to generate a new shortcut for often used +\refKey{/csvsim/sort by} applications. It also adds a more semantic touch. +The new shortcut option is +\tcbox[on line,size=small,colback=white,colframe=red]{|sort by| \meta{name}} which expands to +\tcbox[on line,size=small,colback=white,colframe=red]{|sort by=|\marg{file name}}.\par\medskip + +Consider the following example: +\begin{dispExample} +\csvautotabular[sort by=namesort.xml]{grade.csv} +\end{dispExample} +A good place for setting up a new sorting rule would be inside the preamble: + +\csvset{new sorting rule={name}{namesort.xml}} +\begin{dispListing} +\csvset{new sorting rule={name}{namesort.xml}} +\end{dispListing} + +Now, we can use the new rule: +\begin{dispExample} +\csvautotabular[sort by name]{grade.csv} +\end{dispExample} +\end{docCsvKey} + + +\begin{docCommand}[doc new=2021-06-28]{csvsortingrule}{\marg{name}\marg{file name}} + Identical in function to \refKey{/csvsim/new sorting rule}, see above. +A good place for setting up a new sorting rule would be inside the preamble: + +\csvsortingrule{name}{namesort.xml} +\begin{dispListing} +\csvsortingrule{name}{namesort.xml} +\end{dispListing} + +Now, we can use the new rule: +\begin{dispExample} +\csvautotabular[sort by name]{grade.csv} +\end{dispExample} +\end{docCommand} + + + +\clearpage +\subsection{Data Collection}\label{sec:datacollection} + +|csvsimple-l3| reads and processes a CSV file line by line. Accordingly, the \TeX{} +input stream is filled line by line. +Although this is an efficient procedure, for some applications like tables with +the \ctanpkg{tabularray} package, collecting the data from the CSV file into a macro is needed. +This macro can be given to the target application for further processing. + + +\begin{docCsvKey}[][doc new=2021-07-06]{collect data}{\colOpt{=true\textbar false}}{default |true|, initially |false|} +|csvsimple-l3| provides limited and experimental support to collect the input data +from the CSV file plus user additions into a macro named \refCom{csvdatacollection}. +Setting \refKey{/csvsim/collect data} adds the contents of the following keys +to \refCom{csvdatacollection}: +\begin{itemize} +\item\refKey{/csvsim/after head} +\item\refKey{/csvsim/after line} +\item\refKey{/csvsim/before first line} +\item\refKey{/csvsim/before line} +\item\refKey{/csvsim/late after first line} +\item\refKey{/csvsim/late after head} +\item\refKey{/csvsim/late after last line} +\item\refKey{/csvsim/late after line} +\end{itemize} +Also, the \emph{expanded} content of +\begin{itemize} +\item\refKey{/csvsim/command} +\end{itemize} +is added to \docAuxCommand{csvdatacollection}. +Note that for \refKey{/csvsim/command} special care has to be taken +\emph{what} should be protected from expansion and \emph{what not}. +Observe the following hints for \refKey{/csvsim/command}: +\begin{itemize} +\item For data macros like |\csvcoli| use |\csvexpval\csvcoli| to add + the \emph{value} of this macro to \refCom{csvdatacollection}. + This is optional, if |\csvcoli| contains numbers or text without active + characters, but essential, if it contains macros. +\item \refCom{csvlinetotablerow} is to be used \emph{without} |\csvexpval|. +\item For macros like |\textbf| use |\csvexpnot\textbf| to \emph{prevent} + expansion. +\item Using computations or not expandable conditionals may likely cause + compilation errors. +\end{itemize} + +\begin{dispExample} +\csvreader[ + collect data, + head to column names, + late after line=\\, + late after last line=, + ]{grade.csv}{}{% + \thecsvrow. \csvexpval\givenname\ \csvexpnot\textbf{\csvexpval\name} + } +Collected data:\par +\csvdatacollection +\end{dispExample} + +Note that data collection is \emph{limited} to some special cases and does not +allow to save all possible content. Table options like \refKey{/csvsim/longtable} +are generally not supported with the important exception of \refKey{/csvsim/tabularray} +which uses \refKey{/csvsim/collect data} automatically.\par +See \Fullref{sec:tabularray} for examples. +\end{docCsvKey} + +\clearpage + +\begin{docCsvKey}[][doc new=2021-07-06]{data collection}{=\meta{macro}}{no default, initially \refCom{csvdatacollection}} +Sets the collection macro to an alternative for \refCom{csvdatacollection}. +\begin{dispListing} + data collection = \myData, % instead of \csvdatacollection +\end{dispListing} +\end{docCsvKey} + + +\begin{docCommand}[doc new=2021-07-06]{csvdatacollection}{} + Macro which contains the collected data of a CSV file processed with + \refKey{/csvsim/collect data}. This macro name can be changed by + setting \refKey{/csvsim/data collection}. +\end{docCommand} + + +\begin{docCommand}[doc new=2021-07-06]{csvexpval}{\meta{macro}} + Recovers the content of the given \meta{macro} and prevents further + expansion. This is a wrapper for \docAuxCommand*{exp_not:V}. + Alternatively, |\expandonce| from \ctanpkg{etoolbox} could be used. +\end{docCommand} + + +\begin{docCommand}[doc new=2021-07-06]{csvexpnot}{\meta{macro}} + Prevents the expansion of the given \meta{macro}. This is a wrapper + for \docAuxCommand*{exp_not:N}. + Alternatively, |\noexpand| could be used. +\end{docCommand} + +The following macros can only be used inside keys which are \emph{not} +collected to \refCom{csvdatacollection}, e.g. inside \refKey{/csvsim/after filter}. + +\begin{docCommand}[doc new=2021-07-06]{csvcollectn}{\marg{code}} + Appends the given \meta{code} to \refCom{csvdatacollection}.\\ + This corresponds to \docAuxCommand*{tl_gput_right:Nn}. +\end{docCommand} + + +\begin{docCommand}[doc new=2021-07-06]{csvcollectx}{\marg{code}} + Appends the expansion of the given \meta{code} to \refCom{csvdatacollection}.\\ + This corresponds to \docAuxCommand*{tl_gput_right:Nx}. +\end{docCommand} + +\begin{docCommand}[doc new=2021-07-06]{csvcollectV}{\meta{macro}} + Appends the content of the given \meta{macro} to \refCom{csvdatacollection}.\\ + This corresponds to \docAuxCommand*{tl_gput_right:NV}. +\end{docCommand} + + +\clearpage +\section{String and Number Tests}\label{sec:stringtests}% + +The following string tests are complementing the string tests +from packages like |etoolbox|. They all do the same, i.e., +comparing expanded strings for equality. To some extent, they are +provided for backward compatibility. +\begin{itemize} +\item\refCom{ifcsvstrcmp} may be the most efficient method, because it uses + the native compiler string comparison (if available). +\item\refCom{ifcsvstrequal} does not rely on a compiler. It also is the + fallback implementation for \refCom{ifcsvstrcmp}, if there is no + native comparison method. +\item\refCom{ifcsvprostrequal} is possibly more failsafe than the other two + string tests. It may be used, if strings contain dirty things like |\textbf{A}|. +\end{itemize} +\medskip + +\begin{docCommand}[doc new and updated={2016-07-01}{2021-06-28}]{ifcsvstrcmp}{\marg{stringA}\marg{stringB}\marg{true}\marg{false}} + Compares two strings and executes \meta{true} if they are equal, and \meta{false} otherwise. + The comparison is done using |\str_compare:eNeTF|. + \refCom{ifcsvstrcmp} is expandable. +\end{docCommand} + + +\begin{docCommand}[doc new and updated={2016-07-01}{2021-06-28}]{ifcsvnotstrcmp}{\marg{stringA}\marg{stringB}\marg{true}\marg{false}} + Compares two strings and executes \meta{true} if they are \emph{not} equal, and \meta{false} otherwise. + The implementation uses \refCom{ifcsvstrcmp}. + \refCom{ifcsvstrcmp} is expandable. +\end{docCommand} + + +\begin{docCommand}[doc new and updated={2016-07-01}{2021-06-28}]{ifcsvstrequal}{\marg{stringA}\marg{stringB}\marg{true}\marg{false}} + Compares two strings and executes \meta{true} if they are equal, and \meta{false} otherwise. + The strings are expanded + and the comparison is done using |\tl_if_eq:NNTF|. + \refCom{ifcsvstrequal} is not expandable. +\end{docCommand} + + +\begin{docCommand}[doc new and updated={2016-07-01}{2021-06-28}]{ifcsvprostrequal}{\marg{stringA}\marg{stringB}\marg{true}\marg{false}} + Compares two strings and executes \meta{true} if they are equal, and \meta{false} otherwise. + The strings are expanded with |\protected@edef| + in the test, i.e. parts of the + strings which are protected stay unexpanded. + The comparison is done using |\tl_if_eq:NNTF|. + \refCom{ifcsvprostrequal} is not expandable. +\end{docCommand} + + +The following number tests are wrappers for corresponding \LaTeX3 conditionals. + +\begin{docCommand}[doc new={2021-06-28}]{ifcsvfpcmp}{\marg{floating point expression}\marg{true}\marg{false}} + Evaluates the given \meta{floating point expression} + and executes \meta{true} or \meta{false} appropriately. + The evaluation is done using |\fp_compare:nTF|. + \refCom{ifcsvfpcmp} is expandable. +\end{docCommand} + +\begin{docCommand}[doc new={2021-06-28}]{ifcsvintcmp}{\marg{integer expression}\marg{true}\marg{false}} + Evaluates the given \meta{integer expression} + and executes \meta{true} or \meta{false} appropriately. + The evaluation is done using |\int_compare:nTF|. + \refCom{ifcsvintcmp} is expandable. +\end{docCommand} + + +\clearpage +\section{Hooks}\label{sec:hooks}% +The following hook(s) are present following \LaTeX's hook management. + +\begin{description} +\item[\textcolor{DarkViolet}{\ttfamily csvsimple/csvline}] + \tcbdocmarginnote{\tcbdocnew{2023-05-08}} + This hook adds code after reading + a line into \refCom{csvline} and before processing this line. + The token list \refCom{csvline} may be manipulated with a global assignment.\par + The following example replaces every \verb+"..."+ by \verb+{...}+ to + approximate double-quote processing within \LaTeX. Still, masking of double-quotes + or nesting will not work. +\begin{dispListing} +\AddToHook{csvsimple/csvline} + { + \tl_set_eq:NN \l_tmpa_tl \csvline + \regex_replace_all:nnN { "([^"]+)" } { {\1} } \l_tmpa_tl + \tl_gset_eq:NN \csvline \l_tmpa_tl + } +\end{dispListing} + +\end{description} + + +\clearpage +\section{Examples}% + +\subsection{A Serial Letter}% +In this example, a serial letter is to be written to all persons with +addresses from the following CSV file. Deliberately, the file content is +not given in very pretty format. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{address.csv} +name,givenname,gender,degree,street,zip,location,bonus +Maier,Hans,m,,Am Bachweg 17,10010,Hopfingen,20 + % next line with a comma in curly braces +Huber,Erna,f,Dr.,{Moosstraße 32, Hinterschlag},10020,Örtingstetten,30 +Weißbäck,Werner,m,Prof. Dr.,Brauallee 10,10030,Klingenbach,40 + % this line is ignored % + Siebener , Franz,m, , Blaumeisenweg 12 , 10040 , Pardauz , 50 + % preceding and trailing spaces in entries are removed % +Schmitt,Anton,m,,{\AE{}lfred-Esplanade, T\ae{}g 37}, 10050,\OE{}resung,60 +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{address} + +Firstly, we survey the file content quickly using +|\csvautotabular|. +As can be seen, unfeasible lines are ignored automatically. + +\begin{dispExample} +\tiny\csvautotabular{address.csv} +\end{dispExample} + +Now, we create the serial letter where every feasible data line produces +an own page. Here, we simulate the page by a |tcolorbox| (from the package +|tcolorbox|). +For the gender specific salutations, an auxiliary macro |\ifmale| is +introduced. + +\begin{dispExample} +% this example requires the tcolorbox package +\newcommand{\ifmale}[2]{\ifcsvstrcmp{\gender}{m}{#1}{#2}} + +\csvreader[head to column names]{address.csv}{}{% +\begin{tcolorbox}[colframe=DarkGray,colback=White,arc=0mm,width=(\linewidth-2pt)/2, + equal height group=letter,before=,after=\hfill,fonttitle=\bfseries, + adjusted title={Letter to \name}] + \ifcsvstrcmp{\degree}{}{\ifmale{Mr.}{Ms.}}{\degree}~\givenname~\name\\ + \street\\\zip~\location + \tcblower + {\itshape Dear \ifmale{Sir}{Madam},}\\ + we are pleased to announce you a bonus value of \bonus\%{} + which will be delivered to \location\ soon.\\\ldots +\end{tcolorbox}} +\end{dispExample} + + + +\clearpage +\subsection{A Graphical Presentation}\label{sec:examgrapghpres}% +For this example, we use some artificial statistical data given by a CSV file. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{data.csv} +land,group,amount +Bayern,A,1700 +Baden-Württemberg,A,2300 +Sachsen,B,1520 +Thüringen,A,1900 +Hessen,B,2100 +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{data} + +Firstly, we survey the file content using +|\csvautobooktabular|. + +\begin{dispExample} +% needs the booktabs package +\csvautobooktabular{data.csv} +\end{dispExample} + +The amount values are presented in the following diagram by bars where +the group classification is given using different colors. + +\begin{dispExample} +% This example requires the package tikz +\begin{tikzpicture}[Group/A/.style={left color=red!10,right color=red!20}, + Group/B/.style={left color=blue!10,right color=blue!20}] +\csvreader[head to column names]{data.csv}{}{% + \begin{scope}[yshift=-\thecsvrow cm] + \path [draw,Group/\group] (0,-0.45) + rectangle node[font=\bfseries] {\amount} (\amount/1000,0.45); + \node[left] at (0,0) {\land}; + \end{scope} } +\end{tikzpicture} +\end{dispExample} + + +\clearpage +It would be nice to sort the bars by length, i.\,e.\ to sort the CSV file +by the |amount| column. If the \csvsorter\ program is properly installed, +see Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting}, +this can be done with the following configuration file for \csvsorter: + +\xmllisting{amountsort} + +Now, we just have to add an option |sort by=amountsort.xml|: +\begin{dispExample} +% This example requires the package tikz +% Also, the CSV-Sorter tool has to be installed +\begin{tikzpicture}[Group/A/.style={left color=red!10,right color=red!20}, + Group/B/.style={left color=blue!10,right color=blue!20}] +\csvreader[head to column names,sort by=amountsort.xml]{data.csv}{}{% + \begin{scope}[yshift=-\thecsvrow cm] + \path [draw,Group/\group] (0,-0.45) + rectangle node[font=\bfseries] {\amount} (\amount/1000,0.45); + \node[left] at (0,0) {\land}; + \end{scope} } +\end{tikzpicture} +\end{dispExample} + + + + +\clearpage +Next, we create a pie chart by calling |\csvreader| twice. +In the first step, the total sum of amounts is computed, and in the second +step the slices are drawn. + +\begin{dispExample} +% Modified example from www.texample.net for pie charts +% This example needs the packages tikz, xcolor, calc +\definecolorseries{myseries}{rgb}{step}[rgb]{.95,.85,.55}{.17,.47,.37} +\resetcolorseries{myseries}% + +% a pie slice +\newcommand{\slice}[4]{ + \pgfmathsetmacro{\midangle}{0.5*#1+0.5*#2} + \begin{scope} + \clip (0,0) -- (#1:1) arc (#1:#2:1) -- cycle; + \colorlet{SliceColor}{myseries!!+}% + \fill[inner color=SliceColor!30,outer color=SliceColor!60] (0,0) circle (1cm); + \end{scope} + \draw[thick] (0,0) -- (#1:1) arc (#1:#2:1) -- cycle; + \node[label=\midangle:#4] at (\midangle:1) {}; + \pgfmathsetmacro{\temp}{min((#2-#1-10)/110*(-0.3),0)} + \pgfmathsetmacro{\innerpos}{max(\temp,-0.5) + 0.8} + \node at (\midangle:\innerpos) {#3}; +} + +% sum of amounts +\csvreader[before reading=\def\mysum{0}]{data.csv}{amount=\amount}{% + \pgfmathsetmacro{\mysum}{\mysum+\amount}% +} + +% drawing of the pie chart +\begin{tikzpicture}[scale=3]% +\def\mya{0}\def\myb{0} +\csvreader[head to column names]{data.csv}{}{% + \let\mya\myb + \pgfmathsetmacro{\myb}{\myb+\amount} + \slice{\mya/\mysum*360}{\myb/\mysum*360}{\amount}{\land} +} +\end{tikzpicture}% +\end{dispExample} + + +\clearpage +Finally, the filter option is demonstrated by separating the groups A and B. +Every item is piled upon the appropriate stack. + +\begin{dispExample} +\newcommand{\drawGroup}[2]{% + \def\mya{0}\def\myb{0} + \node[below=3mm] at (2.5,0) {\bfseries Group #1}; + \csvreader[head to column names,filter equal={\group}{#1}]{data.csv}{}{% + \let\mya\myb + \pgfmathsetmacro{\myb}{\myb+\amount} + \path[draw,top color=#2!25,bottom color=#2!50] + (0,\mya/1000) rectangle node{\land\ (\amount)} (5,\myb/1000); +}} + +\begin{tikzpicture} + \fill[gray!75] (-1,0) rectangle (13,-0.1); + \drawGroup{A}{red} + \begin{scope}[xshift=7cm] + \drawGroup{B}{blue} + \end{scope} +\end{tikzpicture} + +\end{dispExample} + + +\clearpage +\subsection{Macro code inside the data}\label{macrocodexample}% + +If needed, the data file may contain macro code. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{macrodata.csv} +type,description,content +M,A nice \textbf{formula}, $\displaystyle \int\frac{1}{x} = \ln|x|+c$ +G,A \textcolor{red}{colored} ball, {\tikz \shadedraw [shading=ball] (0,0) circle (.5cm);} +M,\textbf{Another} formula, $\displaystyle \lim\limits_{n\to\infty} \frac{1}{n}=0$ +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{macrodata} + +Firstly, we survey the file content using +|\csvautobooktabular|. + +\begin{dispExample} +\csvautobooktabular{macrodata.csv} +\end{dispExample} + + +\begin{dispExample} +\csvstyle{my enumerate}{head to column names, + before reading=\begin{enumerate},after reading=\end{enumerate}} + +\csvreader[my enumerate]{macrodata.csv}{}{% + \item \description:\par\content} + +\bigskip +Now, formulas only: +\csvreader[my enumerate,filter strcmp={\type}{M}]{macrodata.csv}{}{% + \item \description:\qquad\content} +\end{dispExample} + +\clearpage +\subsection{Tables with Number Formatting}\label{numberformatting}% + +We consider a file with numerical data which should be pretty-printed. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{data_numbers.csv} +month, dogs, cats +January, 12.50,12.3e5 +February, 3.32, 8.7e3 +March, 43, 3.1e6 +April, 0.33, 21.2e4 +May, 5.12, 3.45e6 +June, 6.44, 6.66e6 +July, 123.2,7.3e7 +August, 12.3, 5.3e4 +September,2.3, 4.4e4 +October, 6.5, 6.5e6 +November, 0.55, 5.5e5 +December, 2.2, 3.3e3 +\end{tcbverbatimwrite} + +\csvlisting{data_numbers} + +\medskip + +The \ctanpkg{siunitx} package provides a huge amount of formatting options for +numbers. A good and robust way to apply formatting by \ctanpkg{siunitx} inside +tables generated by |csvsimple-l3| is the |\tablenum| macro from +\ctanpkg{siunitx}. + +\begin{dispExample} +% \usepackage{siunitx,array,booktabs} +\csvreader[ + head to column names, + before reading = \begin{center}\sisetup{table-number-alignment=center}, + tabular = cc, + table head = \toprule \textbf{Cats} & \textbf{Dogs} \\\midrule, + table foot = \bottomrule, + after reading = \end{center} + ]{data_numbers.csv}{}{% + \tablenum[table-format=2.2e1]{\cats} & \tablenum{\dogs} + } +\end{dispExample} + +\clearpage + +It is also possible to create on-the-fly tables using calculations of +the given data. The following example shows cat values bisected and +dog values doubled. + +\begin{dispExample} +% \usepackage{siunitx,array,booktabs,xfp} +\csvreader[ + head to column names, + before reading = \begin{center}\sisetup{table-number-alignment=center}, + tabular = cccc, + table head = \toprule \textbf{Cats} & \textbf{Dogs} + & \textbf{Halfcats} & \textbf{Doubledogs} \\\midrule, + table foot = \bottomrule, + after reading = \end{center} + ]{data_numbers.csv}{}{% + \tablenum[table-format=2.2e1]{\cats} & \tablenum{\dogs} + & \tablenum[exponent-mode=scientific, round-precision=3, + round-mode=places, table-format=1.3e1]{\fpeval{\cats/2}} + & \tablenum{\fpeval{\dogs*2}} + } +\end{dispExample} + + +\clearpage + +The |siunitx| package also provides a new column type |S| +which can align material using a number of different strategies. +Special care is needed, if the \emph{first} or the \emph{last} column is to be formatted with +the column type |S|. The number detection of |siunitx| is disturbed by +the line reading code of |csvsimple-l3| which actually is present at the +first and last column. To avoid this problem, the utilization of +|\tablenum| is appropriate, see above. +Alternatively, a very nifty workaround suggested by Enrico Gregorio is to +add an invisible dummy column with |c@{}| as first column +and |@{}c| as last column: + +\begin{dispExample} +% \usepackage{siunitx,array,booktabs} +\csvreader[ + head to column names, + before reading = \begin{center}\sisetup{table-number-alignment=center}, + tabular = {c@{}S[table-format=2.2e1]S@{}c}, + table head = \toprule & \textbf{Cats} & \textbf{Dogs} & \\\midrule, + table foot = \bottomrule, + after reading = \end{center} + ]{data_numbers.csv}{}{% + & \cats & \dogs & + } +\end{dispExample} + + + + +\clearpage +Now, the preceding table shall be sorted by the \emph{cats} values. +If the \csvsorter\ program is properly installed, +see Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting}, +this can be done with the following configuration file for \csvsorter: + +\xmllisting{catsort} + +Now, we just have to add an option |sort by=catsort.xml|: +\begin{dispExample} +% \usepackage{siunitx,array,booktabs} +% Also, the CSV-Sorter tool has to be installed +\csvreader[ + head to column names, + sort by = catsort.xml, + before reading = \begin{center}\sisetup{table-number-alignment=center}, + tabular = lcc, + table head = \toprule \textbf{Month} & \textbf{Dogs} & \textbf{Cats} \\\midrule, + table foot = \bottomrule, + after reading = \end{center} + ]{data_numbers.csv}{}{% + \month & \tablenum{\dogs} & \tablenum[table-format=2.2e1]{\cats} + } +\end{dispExample} + + +\clearpage +\subsection{CSV data without header line}\label{noheader}% +CSV files with a header line are more semantic than files without header, +but it's no problem to work with headless files. + +For this example, we use again some artificial statistical data given by a CSV file +but this time without header. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{data_headless.csv} +Bayern,A,1700 +Baden-Württemberg,A,2300 +Sachsen,B,1520 +Thüringen,A,1900 +Hessen,B,2100 +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{data_headless} + +Note that you cannot use the \refKey{/csvsim/no head} option for the auto tabular +commands. +If no options are given, the first line is interpreted as header line +which gives an unpleasant result: + +\begin{dispExample} +\csvautobooktabular{data_headless.csv} +\end{dispExample} + +To get the expected result, the \emph{star} versions of the auto tabular +commands can be used. + +\begin{dispExample} +\csvautobooktabular*{data_headless.csv} +\end{dispExample} + +This example can be extended to insert a table head for this headless data: + +\begin{dispExample} +\csvautobooktabular*[ + table head=\toprule\bfseries Land & \bfseries Group + & \bfseries Amount\\\midrule + ]{data_headless.csv} +\end{dispExample} + + +\clearpage + +For the normal \refCom{csvreader} command, the \refKey{/csvsim/no head} option +should be applied. Of course, we cannot use \refKey{/csvsim/head to column names} +because there is no head, but the columns can be addressed by their numbers: + +\begin{dispExample} +\csvreader[ + no head, + tabular = lr, + table head = \toprule\bfseries Land & \bfseries Amount\\\midrule, + table foot = \bottomrule] + {data_headless.csv} + { 1=\land, 3=\amount } + {\land & \amount} +\end{dispExample} + + + +\clearpage +\subsection{Tables with \texttt{tabularray}}\label{sec:tabularray}% + +The \ctanpkg{tabularray} package gives extended control for generating +tables. \refKey{/csvsim/tabularray} and \refKey{/csvsim/centered tabularray} +support such tables. A distinctiveness is that for \ctanpkg{tabularray} +data from a CSV file has to be \emph{collected} first (into a macro) +and applied afterwards. The process is hidden from the user view, but +has to be taken into account when \refKey{/csvsim/command} is set up, +see \Fullref{sec:datacollection}. + +The following examples uses |data.csv| from \Fullref{sec:examgrapghpres}. + +\begin{dispExample} +% \usepackage{tabularray,siunitx,xfp} +\csvreader[ + head to column names, + centered tabularray = + { + rowsep = 1mm, + colsep = 5mm, + rows = {blue7}, + hlines = {2pt, white}, + vlines = {2pt, white}, + row{1} = {bg=azure3, fg=white, font=\bfseries\large, 8mm}, + }, + table head = {\SetCell[c=4]{c} Important Data Table \\}, + ]{data.csv}{}{ + \ifcsvstrcmp{\group}{A}{\csvexpnot\SetRow{brown7}}{} + \csvexpnot\SetCell{bg=purple7} + \csvexpval\land + & \csvexpval\group + & \csvexpval\amount + & \tablenum[exponent-mode=scientific, round-precision=3, + round-mode=places, table-format=1.3e1]{\fpeval{pi*\amount}} + } +\end{dispExample} + +Note in the example above that +\begin{itemize} +\item \refKey{/csvsim/table head} is \emph{collected} unexpanded, i.e. + |\SetCell| has not to be protected. On the other hand, CSV data could not + be used here. +\item \refKey{/csvsim/command} is \emph{collected} expanded. This is identical + to the mandatory last argument of \refCom{csvreader}. + \begin{itemize} + \item Therefore, expansion of |\SetRow|, |\SetCell|, etc. is prevented by \refCom{csvexpnot}. + \item The \emph{values} (content) of |\land|, |\group|, etc. are recovered by + \refCom{csvexpval}. + \item |\ifcsvstrcmp| and |\fpeval| are \emph{expandable} and therefore the + results of these commands are \emph{collected}. + \item |\tablenum| from \ctanpkg{siunitx} is a robust command and therefore + needs no protection. If you are not sure, if a command is robust or not, it + does not hurt add the prefix \refCom{csvexpnot}, i.e. use |\csvexpnot\tablenum|. + \end{itemize} +\end{itemize} + +\clearpage + +Filters and line ranges can be used for \ctanpkg{tabularray} and all +data collections without restriction: + +\begin{dispExample} +% \usepackage{tabularray} +Display group `A` only:\par +\csvreader[ + head to column names, + filter strcmp = {\group}{A}, + centered tabularray = + { + rowsep = 1mm, + colsep = 5mm, + column{1} = {r, fg=yellow5, colsep=2pt}, + column{2} = {r, yellow8!10, font=\bfseries}, + column{3} = {l, yellow8}, + hlines = {2pt, white}, + }, + ]{data.csv}{}{ + \thecsvrow + & \csvexpval\land + & \csvexpval\amount + } +\end{dispExample} + + +\begin{dispExample} +% \usepackage{tabularray} +Display data from line 3 on:\par +\csvreader[ + head to column names, + range = 3-, + centered tabularray = + { + rowsep = 1mm, + colsep = 5mm, + column{1} = {r, fg=violet5, colsep=2pt}, + column{2} = {r, violet8!10, font=\bfseries}, + column{3} = {l, violet8}, + hlines = {2pt, white}, + }, + ]{data.csv}{}{ + \thecsvrow + & \csvexpval\land + & \csvexpval\amount + } +\end{dispExample} + + +\clearpage + +The following example uses \refCom{csvautotabularray} to display the whole +table. Note that the \ctanpkg{tabularray} options are given as last +optional argument. + +\begin{dispExample} +% \usepackage{tabularray} +\csvautotabularray[table centered]{data.csv} + [ + row{odd} = {blue!85!gray!7}, + row{1} = {blue!50!gray!25, font=\bfseries}, + hline{1,Z} = {0.1em, blue!50!black}, + hline{2} = {blue!50!black} + ] +\end{dispExample} + + + +\clearpage +\subsection{Imported CSV data}\label{sec:importeddata}% +If data is imported from other applications, there is not always a choice +to format in comma separated values with curly brackets. + +Consider the following example data file: + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{imported.csv} +"name";"address";"email" +"Frank Smith";"Yellow Road 123, Brimblsby";"frank.smith@organization.org" +"Mary May";"Blue Alley 2a, London";"mmay@maybe.uk" +"Hans Meier";"Hauptstraße 32, Berlin";"hans.meier@corporation.de" +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{imported} + +If the \csvsorter\ program is properly installed, +see Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting}, +this can be transformed on-the-fly +with the following configuration file for \csvsorter: + +\xmllisting{transform} + +Now, we just have to add an option |sort by=transform.xml| to transform +the input data. Here, we actually do not sort. + +\begin{dispExample} +% \usepackage{booktabs,array} +% Also, the CSV-Sorter tool has to be installed +\newcommand{\Header}[1]{\normalfont\bfseries #1} + +\csvreader[ + sort by = transform.xml, + tabular = >{\itshape}ll>{\ttfamily}l, + table head = \toprule\Header{Name} & \Header{Address} & \Header{email}\\\midrule, + table foot = \bottomrule + ] + {imported.csv}{} + {\csvlinetotablerow} +\end{dispExample} + +The file which is generated on-the-fly and which is actually read by +|csvsimple-l3| is the following: + +\tcbinputlisting{docexample,listing style=tcbdocumentation,fonttitle=\bfseries, + listing only,listing file=\jobname_sorted._csv} + + +\clearpage +\subsection{Encoding}\label{encoding}% +If the CSV file has a different encoding than the \LaTeX\ source file, +then special care is needed. + +\begin{itemize} +\item The most obvious treatment is to change the encoding of the CSV file + or the \LaTeX\ source file to match the other one (every good editor + supports such a conversion). This is the easiest choice, if there a no + good reasons against such a step. E.g., unfortunately, several tools + under Windows need the CSV file to be |cp1252| encoded while + the \LaTeX\ source file may need to be |utf8| encoded. + +\item The |inputenc| package allows to switch the encoding inside the + document, say from |utf8| to |cp1252|. Just be aware that you should only + use pure ASCII for additional texts inside the switched region. +\begin{dispListing} +% !TeX encoding=UTF-8 +% .... +\usepackage[utf8]{inputenc} +% .... +\begin{document} +% .... +\inputencoding{latin1}% only use ASCII from here, e.g. "Uberschrift +\csvreader[%... + ]{data_cp1252.csv}{%... + }{% .... + } +\inputencoding{utf8} +% .... +\end{document} +\end{dispListing} + +\item As a variant to the last method, the encoding switch can be done + using options from |csvsimple-l3|: +\begin{dispListing} +% !TeX encoding=UTF-8 +% .... +\usepackage[utf8]{inputenc} +% .... +\begin{document} +% .... +% only use ASCII from here, e.g. "Uberschrift +\csvreader[%... + before reading=\inputencoding{latin1}, + after reading=\inputencoding{utf8}, + ]{data_cp1252.csv}{%... + }{% .... + } +% .... +\end{document} +\end{dispListing} + +\pagebreak\item +If the \csvsorter\ program is properly installed, +see Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting}, +the CSV file can be re-encoded on-the-fly +with the following configuration file for \csvsorter: + +\xmllisting{encoding} + +\begin{dispListing} +% !TeX encoding=UTF-8 +% .... +\usepackage[utf8]{inputenc} +% .... +\begin{document} +% .... +\csvreader[%... + sort by=encoding.xml, + ]{data_cp1252.csv}{%... + }{% .... + } +% .... +\end{document} +\end{dispListing} + + +\end{itemize} + + +\clearpage + +\printindex + +\end{document} diff --git a/csvsimple/csvsimple-legacy.pdf b/csvsimple/csvsimple-legacy.pdf new file mode 100644 index 0000000..a2b9955 Binary files /dev/null and b/csvsimple/csvsimple-legacy.pdf differ diff --git a/csvsimple/csvsimple-legacy.sty b/csvsimple/csvsimple-legacy.sty new file mode 100644 index 0000000..f47d754 --- /dev/null +++ b/csvsimple/csvsimple-legacy.sty @@ -0,0 +1,795 @@ +%% The LaTeX package csvsimple - version 2.5.0 (2023/10/16) +%% csvsimple-legacy.sty: Simple LaTeX CSV file processing (LaTeX2e) +%% +%% ------------------------------------------------------------------------------------------- +%% Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm +%% ------------------------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `author-maintained'. +%% +%% This work consists of all files listed in README.md +%% +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{csvsimple-legacy}[2023/10/16 version 2.5.0 LaTeX2e CSV file processing] + + +%---- check package +\ExplSyntaxOn +\cs_if_exist:NT \c__csvsim_package_expl_bool + { + \msg_new:nnn { csvsimple }{ legacy / package-loaded } + { Package~'csvsimple-l3'~seems~already~be~loaded!~ + 'csvsimple-legacy'~cannot~be~loaded~simultaneously.~ + Therefore,~loading~of~'csvsimple-legacy'~stops~now.} + \msg_warning:nn { csvsimple }{ legacy / package-loaded } + \tex_endinput:D + } +\bool_const:Nn \c__csvsim_package_expl_bool { \c_false_bool } +\ExplSyntaxOff + + + +\RequirePackage{pgfrcs,pgfkeys,ifthen,etoolbox,shellesc} + + +%---- general + +\def\csv@warning#1{\PackageWarning{csvsimple}{#1}} +\def\csv@error#1#2{\PackageError{csvsimple}{#1}{#2}} + +\newread\csv@file +\newcounter{csvinputline} +\newcounter{csvrow} +\newcounter{csvcol} + +\def\csv@empty{} + +\long\def\csviffirstrow#1#2{% + \ifnum\c@csvrow=1% + \long\def\csviffirstrow@doit{#1}% + \else% + \long\def\csviffirstrow@doit{#2}% + \fi% + \csviffirstrow@doit% +} + +\long\def\csvifoddrow#1#2{% + \ifodd\c@csvrow% + \long\def\csvifoddrow@doit{#1}% + \else% + \long\def\csvifoddrow@doit{#2}% + \fi% + \csvifoddrow@doit% +} + +\def\csv@assemble@csvlinetotablerow{% + \global\c@csvcol 1\relax% + \xdef\csvlinetotablerow{\expandonce{\csname csvcol\romannumeral\c@csvcol\endcsname}}% + \ifnum\c@csvcol<\csv@columncount\relax% + \loop% + \global\advance\c@csvcol 1\relax% + \xappto\csvlinetotablerow{\noexpand&\expandonce{\csname csvcol\romannumeral\c@csvcol\endcsname}}% + \ifnum\c@csvcol<\csv@columncount\relax\repeat% + \fi% + \csvlinetotablerow% +} + + +%---- breaking lines + +% This command removes leading and trailing spaces from . I found +% the original code on the web. The original author was Michael Downes, who +% provided the code as an answer to 'around the bend' question #15. +\catcode`\Q=3 +\def\csv@TrimSpaces#1{% + \begingroup% + \aftergroup\toks\aftergroup0\aftergroup{% + \expandafter\csv@trimb\expandafter\noexpand#1Q Q}% + \global\edef#1{\the\toks0}% +} +\def\csv@trimb#1 Q{\csv@trimc#1Q} +\def\csv@trimc#1Q#2{\afterassignment\endgroup \vfuzz\the\vfuzz#1} +\catcode`\Q=11 + +\def\csv@TrimBraces#1{\expandafter\csv@TrimBraces@#1\@nil{#1}} +\def\csv@TrimBraces@#1\@nil#2{\def#2{#1}} + +\def\csv@breakline@kernel#1{% + \ifx\csv@termination#1\let\nextcol=\relax\else% + \let\nextcol=\csv@breakline% + \global\advance\c@csvcol 1\relax% + \def\csv@col@body{#1}% + \csv@TrimSpaces\csv@col@body% + \csv@TrimBraces\csv@col@body% + \toks@\expandafter{\csv@col@body}% + \expandafter\xdef\csname csvcol\romannumeral\c@csvcol\endcsname{\the\toks@}% + \fi% + \nextcol% +} + +% comma +\def\csv@breakline@A#1,{\csv@breakline@kernel{#1}} + +\def\csv@scanline@A#1{% + \global\c@csvcol 0\relax% + \csv@breakline#1,\csv@termination,% +} + +% semi colon +\def\csv@breakline@B#1;{\csv@breakline@kernel{#1}} + +\def\csv@scanline@B#1{% + \global\c@csvcol 0\relax% + \csv@breakline#1;\csv@termination;% +} + +% pipe +\def\csv@breakline@C#1|{\csv@breakline@kernel{#1}} + +\def\csv@scanline@C#1{% + \global\c@csvcol 0\relax% + \csv@breakline#1|\csv@termination|% +} + +% tab +\catcode`\^^I=12 +\def\csv@breakline@D#1^^I{\csv@breakline@kernel{#1}} + +\def\csv@scanline@D#1{% + \global\c@csvcol 0\relax% + \csv@breakline#1^^I\csv@termination^^I% +} +\catcode`\^^I=10 + +% expands a CSV line and scans content +\def\csv@escanline#1{% + \toks@\expandafter{#1}% + \edef\@csv@scanline{\noexpand\csv@scanline{\the\toks@}}% + \@csv@scanline% +} + +{ + \catcode`\"=12% + \gdef\csv@passivquotes{"} +} + +\newwrite\csv@out + +\def\csv@preprocessor@csvsorter#1#2#3{% + \begingroup% + \typeout{}% + \immediate\openout\csv@out=\csv@csvsorter@token% + \immediate\write\csv@out{\string\makeatletter\string\csv@error{Call of CSV-Sorter failed! Use '-shell-escape' option or check log file '\csv@csvsorter@log'.}{}}% + \immediate\closeout\csv@out% + \ShellEscape{\csv@csvsorter@command\space + -c \csv@passivquotes#1\csv@passivquotes\space + -l \csv@passivquotes\csv@csvsorter@log\csv@passivquotes\space + -t \csv@passivquotes\csv@csvsorter@token\csv@passivquotes\space + -i \csv@passivquotes#2\csv@passivquotes\space + -o \csv@passivquotes#3\csv@passivquotes\space -q 1}% + \input{\csv@csvsorter@token}% + \endgroup% +} + + +\def\csv@preprocss@none{% + \let\csv@input@filename=\csv@filename% +} + +\def\csv@preprocss@procedure{% + \csv@preprocessor{\csv@filename}{\csv@ppfilename}% + \let\csv@input@filename=\csv@ppfilename% +} + + +%---- the loop + +\def\csv@AtEndLoop{\gappto\@endloophook} +\let\@endloophook\csv@empty + +\def\csv@current@col{\csname csvcol\romannumeral\c@csvcol\endcsname} + +% auto head names +\def\set@csv@autohead{% + \toks0=\expandafter{\csname\csv@headnameprefix\csv@current@col\endcsname}% + \toks1=\expandafter{\csname csvcol\romannumeral\c@csvcol\endcsname}% + \begingroup\edef\csv@temp{\endgroup\noexpand\gdef\the\toks0{\the\toks1}\noexpand\csv@AtEndLoop{\noexpand\gdef\the\toks0{}}}% + \csv@temp% +} + +% head names and numbers +\def\set@csv@head{% + \toks0={\gdef##1}% + \toks1=\expandafter{\csname csvcol\romannumeral\c@csvcol\endcsname}% + \begingroup\edef\csv@temp{\endgroup\noexpand\pgfkeysdef{/csv head/\csv@current@col}{\the\toks0{\the\toks1}\noexpand\csv@AtEndLoop{\the\toks0{}}}}% + \csv@temp% + \begingroup\edef\csv@temp{\endgroup\noexpand\pgfkeysdef{/csv head/\thecsvcol}{\the\toks0{\the\toks1}\noexpand\csv@AtEndLoop{\the\toks0{}}}}% + \csv@temp% +} + +% head line +\def\csv@processheadline{% + \csvreadnext% + \ifx\csv@par\csvline\relax% + \csv@error{File '\csv@input@filename' starts with an empty line!}{}% + \else\csv@escanline{\csvline}% + \fi% + \xdef\csv@columncount{\thecsvcol}% + \global\c@csvcol 0\relax% + \loop% + \global\advance\c@csvcol 1\relax% + \csv@opt@headtocolumnames% + \set@csv@head% + \ifnum\c@csvcol<\csv@columncount\repeat% + \toks@=\expandafter{\csv@columnnames}% + \edef\csv@processkeys{\noexpand\pgfkeys{/csv head/.cd,\the\toks@}}% + \csv@processkeys% + \csv@posthead% +} + +% head numbers for no head +\def\set@csv@nohead{% + \toks0={\gdef##1}% + \toks1=\expandafter{\csname csvcol\romannumeral\c@csvcol\endcsname}% + \begingroup\edef\csv@temp{\endgroup\noexpand\pgfkeysdef{/csv head/\thecsvcol}{\the\toks0{\the\toks1}\noexpand\csv@AtEndLoop{\the\toks0{}}}}% + \csv@temp% +} + +% no head line +\def\csv@noheadline{% + \global\c@csvcol 0\relax% + \loop% + \global\advance\c@csvcol 1\relax% + \set@csv@nohead% + \ifnum\c@csvcol<\csv@columncount\repeat% + \toks@=\expandafter{\csv@columnnames}% + \edef\csv@processkeys{\noexpand\pgfkeys{/csv head/.cd,\the\toks@}}% + \csv@processkeys% +} + +% check filter +\def\csv@checkfilter{% + \csv@prefiltercommand% + \csv@iffilter{% + \stepcounter{csvrow}% + \let\csv@usage=\csv@do@linecommand% + }{}% +} + +\def\csv@truefilter#1#2{#1} + +\def\csv@falsefilter#1#2{#2} + +\def\csvfilteraccept{\global\let\csv@iffilter=\csv@truefilter} + +\def\csvfilterreject{\global\let\csv@iffilter=\csv@falsefilter} + +% check columns +\def\csv@checkcolumncount{% + \ifnum\c@csvcol=\csv@columncount\relax% + \csv@checkfilter% + \else% + \csv@columncounterror% + \fi% +} + +\def\csv@nocheckcolumncount{% + \csv@checkfilter% +} + +% normal line +\def\csv@do@linecommand{% + \csv@do@latepostline% + \csv@do@preline% + \csv@body\relax% + \csv@do@postline% +} + +\gdef\csvreadnext{% + \global\read\csv@file to\csvline% + \stepcounter{csvinputline}% +} + +\def\csv@par{\par} + +% reads and processes a CSV file +\long\def\csvloop#1{% + % reset + \global\let\@endloophook\csv@empty% + \global\let\csvlinetotablerow\csv@assemble@csvlinetotablerow% + % options + \csvset{default,every csv,#1}% + \csv@preprocss% + \csv@set@catcodes% + \csv@prereading% + \csv@table@begin% + \setcounter{csvinputline}{0}% + % start reading + \openin\csv@file=\csv@input@filename\relax% + \ifeof\csv@file% + \csv@error{File '\csv@input@filename' not existent, not readable, or empty!}{}% + \else% + % the head line + \csv@opt@processheadline% + \fi% + % + \setcounter{csvrow}{0}% + \gdef\csv@do@preline{% + \csv@prefirstline% + \global\let\csv@do@preline=\csv@preline% + }% + \gdef\csv@do@postline{% + \csv@postfirstline% + \global\let\csv@do@postline=\csv@postline% + }% + \gdef\csv@do@@latepostline{% + \csv@latepostfirstline% + \global\let\csv@do@latepostline=\csv@latepostline% + }% + \gdef\csv@do@latepostline{% + \csv@lateposthead% + \global\let\csv@do@latepostline=\csv@do@@latepostline% + }% + % command for the reading loop + \gdef\csv@iterate{% + \let\csv@usage=\csv@empty% + \csvreadnext% + \ifeof\csv@file% + \global\let\csv@next=\csv@empty% + \else% + \global\let\csv@next=\csv@iterate% + \ifx\csv@par\csvline\relax% + \else% + \csv@escanline{\csvline}% + % check and decide + \csv@opt@checkcolumncount% + \fi% + \fi% + % do or do not + \csv@usage% + \csv@next}% + \ifeof\csv@file% + \global\let\csv@next=\csv@empty% + \else% + \global\let\csv@next=\csv@iterate% + \fi% + \csv@next% + \closein\csv@file% + \@endloophook% + \csv@latepostlastline% + \csv@table@end% + \csv@postreading% + \csv@reset@catcodes% +} + +% user command +\long\def\csv@reader[#1]#2#3#4{% + \global\long\def\csv@@body{#4}% + \csvloop{#1,file={#2},column names={#3},command=\csv@@body}% +} + +\def\csvreader{% + \@ifnextchar[{\csv@reader}{\csv@reader[]}} + + +%---- keys + +\pgfkeys{/handlers/.gstore in/.code=\pgfkeysalso{\pgfkeyscurrentpath/.code=\gdef#1{##1}}} +\pgfkeys{/csv/.is family} +\pgfkeys{/csv head/.is family} + +\def\csvset{\pgfqkeys{/csv}} +\def\csvheadset{\pgfqkeys{/csv head}} + +\csvset{% + file/.gstore in=\csv@filename,% + preprocessed file/.gstore in=\csv@ppfilename,% + preprocessor/.code={\gdef\csv@preprocessor{#1}\let\csv@preprocss=\csv@preprocss@procedure},% + no preprocessing/.code={\let\csv@preprocss=\csv@preprocss@none}, + column names reset/.code={\gdef\csv@columnnames{}},% + column names/.code={% + \toks0=\expandafter{\csv@columnnames}% + \def\temp{#1}\toks1=\expandafter{\temp}% + \xdef\csv@columnnames{\the\toks0,\the\toks1}% + }, + command/.gstore in=\csv@body,% + check column count/.is choice,% + check column count/.default=true,% + check column count/true/.code={\global\let\csv@opt@checkcolumncount=\csv@checkcolumncount},% + check column count/false/.code={\global\let\csv@opt@checkcolumncount=\csv@nocheckcolumncount},% + on column count error/.gstore in=\csv@columncounterror, + head/.is choice,% + head/.default=true,% + head/true/.code={\global\let\csv@opt@processheadline=\csv@processheadline% + \pgfkeysalso{check column count}},% + head/false/.code={\global\let\csv@opt@processheadline=\csv@noheadline% + \pgfkeysalso{check column count=false,late after head=}},% + head to column names prefix/.store in=\csv@headnameprefix,% + head to column names/.is choice,% + head to column names/.default=true,% + head to column names/true/.code={\global\let\csv@opt@headtocolumnames=\set@csv@autohead},% + head to column names/false/.code={\global\let\csv@opt@headtocolumnames=\csv@empty},% + column count/.gstore in=\csv@columncount,% + filter/.code={\gdef\csv@iffilter{\ifthenelse{#1}}}, + filter ifthen/.code={\gdef\csv@iffilter{\ifthenelse{#1}}}, + filter test/.code={\gdef\csv@iffilter{#1}}, + filter expr/.code={\gdef\csv@iffilter{\ifboolexpr{#1}}}, + no filter/.code={\csvfilteraccept}, + filter reject all/.code={\csvfilterreject}, + filter accept all/.code={\csvfilteraccept}, + before filter/.gstore in=\csv@prefiltercommand, + full filter/.gstore in=\csv@prefiltercommand, + before first line/.gstore in=\csv@prefirstline, + before line/.code={\gdef\csv@preline{#1}\pgfkeysalso{before first line=#1}}, + after first line/.gstore in=\csv@postfirstline, + after line/.code={\gdef\csv@postline{#1}\pgfkeysalso{after first line=#1}}, + late after first line/.gstore in=\csv@latepostfirstline, + late after last line/.gstore in=\csv@latepostlastline, + late after line/.code={\gdef\csv@latepostline{#1}\pgfkeysalso{late after first line=#1,late after last line=#1}}, + after head/.gstore in=\csv@posthead, + late after head/.gstore in=\csv@lateposthead, + before reading/.gstore in=\csv@prereading, + after reading/.gstore in=\csv@postreading, + before table/.gstore in=\csv@pretable, + after table/.gstore in=\csv@posttable, + table head/.gstore in=\csv@tablehead, + table foot/.gstore in=\csv@tablefoot, + @table/.code 2 args={\gdef\csv@table@begin{#1}\gdef\csv@table@end{#2}}, + no table/.style={@table={}{}}, + separator/.is choice, + separator/comma/.code={\global\let\csv@scanline=\csv@scanline@A% + \global\let\csv@breakline\csv@breakline@A}, + separator/semicolon/.code={\global\let\csv@scanline=\csv@scanline@B% + \global\let\csv@breakline\csv@breakline@B}, + separator/pipe/.code={\global\let\csv@scanline=\csv@scanline@C% + \global\let\csv@breakline\csv@breakline@C}, + separator/tab/.code={\global\let\csv@scanline=\csv@scanline@D% + \global\let\csv@breakline\csv@breakline@D% + \csvset{respect tab}}, + % + csvsorter command/.store in=\csv@csvsorter@command, + csvsorter configpath/.store in=\csv@csvsorter@configpath, + sort by/.style={preprocessor={\csv@preprocessor@csvsorter{\csv@csvsorter@configpath/#1}}}, + new sorting rule/.style 2 args={sort by #1/.style={sort by={#2}}}, + csvsorter log/.store in=\csv@csvsorter@log, + csvsorter token/.store in=\csv@csvsorter@token, + csvsorter command=csvsorter, + csvsorter configpath=., + preprocessed file={\jobname_sorted._csv}, + csvsorter log={csvsorter.log}, + csvsorter token={\jobname.csvtoken}, + % + % default for reset + default/.style={ + file=unknown.csv, + no preprocessing, + command=\csvline, + column names reset, + head, + head to column names prefix=, + head to column names=false, + column count=10, + on column count error=, + no filter, + before filter=, + before line=, + after line=, + late after line=, + after head=, + late after head=, + before reading=, + after reading=, + before table=, + after table=, + table head=, + table foot=, + no table, + separator=comma, + }, + default, + % + % styles + every csv/.style={}, + no head/.style={head=false}, + no check column count/.style={check column count=false}, + warn on column count error/.style={on column count error={\csv@warning{>\thecsvcol< instead of >\csv@columncount< columns for input line >\thecsvinputline< of file >\csv@ppfilename<}}}, + filter equal/.style 2 args={filter ifthen=\equal{#1}{#2}}, + filter not equal/.style 2 args={filter ifthen=\not\equal{#1}{#2}}, + filter strcmp/.style 2 args={filter test=\ifcsvstrcmp{#1}{#2}}, + filter not strcmp/.style 2 args={filter test=\ifcsvnotstrcmp{#1}{#2}}, + tabular/.style={ + @table={\csv@pretable\begin{tabular}{#1}\csv@tablehead}{\csv@tablefoot\end{tabular}\csv@posttable}, + late after line=\\}, + centered tabular/.style={ + @table={\begin{center}\csv@pretable\begin{tabular}{#1}\csv@tablehead}{\csv@tablefoot\end{tabular}\csv@posttable\end{center}}, + late after line=\\}, + longtable/.style={ + @table={\csv@pretable\begin{longtable}{#1}\csv@tablehead}{\csv@tablefoot\end{longtable}\csv@posttable}, + late after line=\\}, + tabbing/.style={ + @table={\csv@pretable\begin{tabbing}\csv@tablehead}{\csv@tablefoot\end{tabbing}\csv@posttable}, + late after line=\\, + late after last line=}, + centered tabbing/.style={ + @table={\begin{center}\csv@pretable\begin{tabbing}\csv@tablehead}{\csv@tablefoot\end{tabbing}\csv@posttable\end{center}}, + late after line=\\, + late after last line=}, + autotabular/.style={ + file=#1, + after head=\csv@pretable\begin{tabular}{|*{\csv@columncount}{l|}}\csv@tablehead, + table head=\hline\csvlinetotablerow\\\hline, + late after line=\\, + table foot=\\\hline, + late after last line=\csv@tablefoot\end{tabular}\csv@posttable, + command=\csvlinetotablerow}, + autolongtable/.style={ + file=#1, + after head=\csv@pretable\begin{longtable}{|*{\csv@columncount}{l|}}\csv@tablehead, + table head=\hline\csvlinetotablerow\\\hline\endhead\hline\endfoot, + late after line=\\, + late after last line=\csv@tablefoot\end{longtable}\csv@posttable, + command=\csvlinetotablerow}, + autobooktabular/.style={ + file=#1, + after head=\csv@pretable\begin{tabular}{*{\csv@columncount}{l}}\csv@tablehead, + table head=\toprule\csvlinetotablerow\\\midrule, + late after line=\\, + table foot=\\\bottomrule, + late after last line=\csv@tablefoot\end{tabular}\csv@posttable, + command=\csvlinetotablerow}, + autobooklongtable/.style={ + file=#1, + after head=\csv@pretable\begin{longtable}{*{\csv@columncount}{l}}\csv@tablehead, + table head=\toprule\csvlinetotablerow\\\midrule\endhead\bottomrule\endfoot, + late after line=\\, + late after last line=\csv@tablefoot\end{longtable}\csv@posttable, + command=\csvlinetotablerow}, +} + +% deprecated keys +\csvset{ + nofilter/.style=no filter, + nohead/.style=no head, +} + +% catcodes +\def\csv@set@catcodes{% + \csv@catcode@tab@set% + \csv@catcode@tilde@set% + \csv@catcode@circumflex@set% + \csv@catcode@underscore@set% + \csv@catcode@and@set% + \csv@catcode@sharp@set% + \csv@catcode@dollar@set% + \csv@catcode@backslash@set% + \csv@catcode@leftbrace@set% + \csv@catcode@rightbrace@set% + \csv@catcode@percent@set} + +\def\csv@reset@catcodes{\csv@catcode@percent@reset% + \csv@catcode@rightbrace@reset% + \csv@catcode@leftbrace@reset% + \csv@catcode@backslash@reset% + \csv@catcode@dollar@reset% + \csv@catcode@sharp@reset% + \csv@catcode@and@reset% + \csv@catcode@underscore@reset% + \csv@catcode@circumflex@reset% + \csv@catcode@tilde@reset% + \csv@catcode@tab@reset% +} + + +\csvset{ + respect tab/.is choice, + respect tab/true/.code={% + \gdef\csv@catcode@tab@set{% + \xdef\csv@catcode@tab@value{\the\catcode`\^^I}% + \catcode`\^^I=12}% + \gdef\csv@catcode@tab@reset{\catcode`\^^I=\csv@catcode@tab@value}}, + respect tab/false/.code={% + \global\let\csv@catcode@tab@set\csv@empty% + \global\let\csv@catcode@tab@reset\csv@empty}, + respect tab/.default=true, + % + respect percent/.is choice, + respect percent/true/.code={% + \gdef\csv@catcode@percent@set{% + \xdef\csv@catcode@percent@value{\the\catcode`\%}% + \catcode`\%=12}% + \gdef\csv@catcode@percent@reset{\catcode`\%=\csv@catcode@percent@value}}, + respect percent/false/.code={% + \global\let\csv@catcode@percent@set\csv@empty% + \global\let\csv@catcode@percent@reset\csv@empty}, + respect percent/.default=true, + % + respect sharp/.is choice, + respect sharp/true/.code={% + \gdef\csv@catcode@sharp@set{% + \xdef\csv@catcode@sharp@value{\the\catcode`\#}% + \catcode`\#=12}% + \gdef\csv@catcode@sharp@reset{\catcode`\#=\csv@catcode@sharp@value}}, + respect sharp/false/.code={% + \global\let\csv@catcode@sharp@set\csv@empty% + \global\let\csv@catcode@sharp@reset\csv@empty}, + respect sharp/.default=true, + % + respect dollar/.is choice, + respect dollar/true/.code={% + \gdef\csv@catcode@dollar@set{% + \xdef\csv@catcode@dollar@value{\the\catcode`\$}% + \catcode`\$=12}% + \gdef\csv@catcode@dollar@reset{\catcode`\$=\csv@catcode@dollar@value}}, + respect dollar/false/.code={% + \global\let\csv@catcode@dollar@set\csv@empty% + \global\let\csv@catcode@dollar@reset\csv@empty}, + respect dollar/.default=true, + % + respect and/.is choice, + respect and/true/.code={% + \gdef\csv@catcode@and@set{% + \xdef\csv@catcode@and@value{\the\catcode`\&}% + \catcode`\&=12}% + \gdef\csv@catcode@and@reset{\catcode`\&=\csv@catcode@and@value}}, + respect and/false/.code={% + \global\let\csv@catcode@and@set\csv@empty% + \global\let\csv@catcode@and@reset\csv@empty}, + respect and/.default=true, + % + respect backslash/.is choice, + respect backslash/true/.code={% + \gdef\csv@catcode@backslash@set{% + \xdef\csv@catcode@backslash@value{\the\catcode`\\}% + \catcode`\\=12}% + \gdef\csv@catcode@backslash@reset{\catcode`\\=\csv@catcode@backslash@value}}, + respect backslash/false/.code={% + \global\let\csv@catcode@backslash@set\csv@empty% + \global\let\csv@catcode@backslash@reset\csv@empty}, + respect backslash/.default=true, + % + respect underscore/.is choice, + respect underscore/true/.code={% + \gdef\csv@catcode@underscore@set{% + \xdef\csv@catcode@underscore@value{\the\catcode`\_}% + \catcode`\_=12}% + \gdef\csv@catcode@underscore@reset{\catcode`\_=\csv@catcode@underscore@value}}, + respect underscore/false/.code={% + \global\let\csv@catcode@underscore@set\csv@empty% + \global\let\csv@catcode@underscore@reset\csv@empty}, + respect underscore/.default=true, + % + respect tilde/.is choice, + respect tilde/true/.code={% + \gdef\csv@catcode@tilde@set{% + \xdef\csv@catcode@tilde@value{\the\catcode`\~}% + \catcode`\~=12}% + \gdef\csv@catcode@tilde@reset{\catcode`\~=\csv@catcode@tilde@value}}, + respect tilde/false/.code={% + \global\let\csv@catcode@tilde@set\csv@empty% + \global\let\csv@catcode@tilde@reset\csv@empty}, + respect tilde/.default=true, + % + respect circumflex/.is choice, + respect circumflex/true/.code={% + \gdef\csv@catcode@circumflex@set{% + \xdef\csv@catcode@circumflex@value{\the\catcode`\^}% + \catcode`\^=12}% + \gdef\csv@catcode@circumflex@reset{\catcode`\^=\csv@catcode@circumflex@value}}, + respect circumflex/false/.code={% + \global\let\csv@catcode@circumflex@set\csv@empty% + \global\let\csv@catcode@circumflex@reset\csv@empty}, + respect circumflex/.default=true, + % + respect leftbrace/.is choice, + respect leftbrace/true/.code={% + \gdef\csv@catcode@leftbrace@set{% + \xdef\csv@catcode@leftbrace@value{\the\catcode`\{}% + \catcode`\{=12}% + \gdef\csv@catcode@leftbrace@reset{\catcode`\{=\csv@catcode@leftbrace@value}}, + respect leftbrace/false/.code={% + \global\let\csv@catcode@leftbrace@set\csv@empty% + \global\let\csv@catcode@leftbrace@reset\csv@empty}, + respect leftbrace/.default=true, + % + respect rightbrace/.is choice, + respect rightbrace/true/.code={% + \gdef\csv@catcode@rightbrace@set{% + \xdef\csv@catcode@rightbrace@value{\the\catcode`\}}% + \catcode`\}=12}% + \gdef\csv@catcode@rightbrace@reset{\catcode`\}=\csv@catcode@rightbrace@value}}, + respect rightbrace/false/.code={% + \global\let\csv@catcode@rightbrace@set\csv@empty% + \global\let\csv@catcode@rightbrace@reset\csv@empty}, + respect rightbrace/.default=true, + % + respect all/.style={respect tab,respect percent,respect sharp,respect dollar, + respect and,respect backslash,respect underscore,respect tilde,respect circumflex, + respect leftbrace,respect rightbrace}, + respect none/.style={respect tab=false,respect percent=false,respect sharp=false, + respect dollar=false,respect and=false,respect backslash=false, + respect underscore=false,respect tilde=false,respect circumflex=false, + respect leftbrace=false,respect rightbrace=false}, + respect none +} + + +\long\def\csv@autotabular[#1]#2{\csvloop{autotabular={#2},#1}} + +\def\csvautotabular{% + \@ifnextchar[{\csv@autotabular}{\csv@autotabular[]}} + +\long\def\csv@autolongtable[#1]#2{\csvloop{autolongtable={#2},#1}} + +\def\csvautolongtable{% + \@ifnextchar[{\csv@autolongtable}{\csv@autolongtable[]}} + +\long\def\csv@autobooktabular[#1]#2{\csvloop{autobooktabular={#2},#1}} + +\def\csvautobooktabular{% + \@ifnextchar[{\csv@autobooktabular}{\csv@autobooktabular[]}} + + +\long\def\csv@autobooklongtable[#1]#2{\csvloop{autobooklongtable={#2},#1}} + +\def\csvautobooklongtable{% + \@ifnextchar[{\csv@autobooklongtable}{\csv@autobooklongtable[]}} + + +\def\csvstyle#1#2{\csvset{#1/.style={#2}}} + +\def\csvnames#1#2{\csvset{#1/.style={column names={#2}}}} + +% string comparison + +\newrobustcmd{\ifcsvstrequal}[2]{% + \begingroup% + \edef\csv@tempa{#1}% + \edef\csv@tempb{#2}% + \ifx\csv@tempa\csv@tempb% + \aftergroup\@firstoftwo% + \else% + \aftergroup\@secondoftwo% + \fi% + \endgroup% +}% + +\newrobustcmd{\ifcsvprostrequal}[2]{% + \begingroup% + \protected@edef\csv@tempa{#1}% + \protected@edef\csv@tempb{#2}% + \ifx\csv@tempa\csv@tempb% + \aftergroup\@firstoftwo% + \else% + \aftergroup\@secondoftwo% + \fi% + \endgroup% +}% + +\AtBeginDocument{% + \ifdefined\pdfstrcmp% + \let\csv@strcmp\pdfstrcmp% + \else\ifdefined\pdf@strcmp% + \let\csv@strcmp\pdf@strcmp% + \fi\fi% + \ifdefined\csv@strcmp% + \newrobustcmd{\ifcsvstrcmp}[2]{% + \ifnum\csv@strcmp{#1}{#2}=\z@\relax% + \expandafter\@firstoftwo% + \else% + \expandafter\@secondoftwo% + \fi% + }% + \else% + \let\ifcsvstrcmp\ifcsvstrequal% + \fi% +} + +\newrobustcmd{\ifcsvnotstrcmp}[4]{\ifcsvstrcmp{#1}{#2}{#4}{#3}} diff --git a/csvsimple/csvsimple-legacy.tex b/csvsimple/csvsimple-legacy.tex new file mode 100644 index 0000000..290d512 --- /dev/null +++ b/csvsimple/csvsimple-legacy.tex @@ -0,0 +1,1970 @@ +% \LaTeX-Main\ +% !TeX encoding=UTF-8 +%% The LaTeX package csvsimple - version 2.5.0 (2023/10/16) +%% csvsimple.tex: Manual +%% +%% ------------------------------------------------------------------------------------------- +%% Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm +%% ------------------------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `author-maintained'. +%% +%% This work consists of all files listed in README.md +%% +\documentclass[a4paper,11pt]{ltxdoc} +\usepackage{csvsimple-doc} + +\usepackage{\csvpkgprefix csvsimple-legacy} + +\tcbmakedocSubKey{docCsvKey}{csv} + +\hypersetup{ + pdftitle={Manual for the csvsimple-legacy package}, + pdfauthor={Thomas F. Sturm}, + pdfsubject={csv file processing with LaTeX2e}, + pdfkeywords={csv file, comma separated values, key value syntax} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\begin{document} +\begin{center} +\begin{tcolorbox}[enhanced,hbox,tikznode,left=8mm,right=8mm,boxrule=0.4pt, + colback=white,colframe=black!50!yellow, + drop lifted shadow=black!50!yellow,arc is angular, + before=\par\vspace*{5mm},after=\par\bigskip] +{\bfseries\LARGE The \texttt{csvsimple-legacy} package}\\[3mm] +{\large Manual for version \version\ (\datum)} +\end{tcolorbox} +{\large Thomas F.~Sturm% + \footnote{Prof.~Dr.~Dr.~Thomas F.~Sturm, Institut f\"{u}r Mathematik und Informatik, + University of the Bundeswehr Munich, D-85577 Neubiberg, Germany; + email: \href{mailto:thomas.sturm@unibw.de}{thomas.sturm@unibw.de}}\par\medskip +\normalsize\url{https://www.ctan.org/pkg/csvsimple}\par +\url{https://github.com/T-F-S/csvsimple} +} +\end{center} +\bigskip +\begin{absquote} + \begin{center}\bfseries Abstract\end{center} + |csvsimple(-legacy)| provides a simple \LaTeX\ interface for the processing of files with + comma separated values (CSV). |csvsimple-legacy| relies heavily on the key value + syntax from |pgfkeys| which results in an easy way of usage. + Filtering and table generation is especially supported. Since the package + is considered as a lightweight tool, there is no support for data sorting + or data base storage. +\end{absquote} + + +\begin{tcolorbox}[enhanced,left=8mm,right=8mm,boxrule=2pt,boxsep=3mm, + colback=red!85!gray!5!white,colframe=red!85!gray, + arc is angular,arc=5mm, + before skip=1cm] +Actually, |csvsimple-legacy| is identical to the old version 1.22 (2021/06/07) +of |csvsimple|. It is superseded by |csvsimple-l3|, a \LaTeX3 implementation +of |csvsimple| which is a \emph{nearly} drop-in for the erstwhile implementation. +\begin{itemize} +\item If you are a new user or an experienced user of |csvsimple| creating a + new document, you are encouraged to turn to |csvsimple-l3|, see\\ + \href{csvsimple-l3.pdf}{\flqq The |csvsimple-l3| package\frqq} +\item If you used |csvsimple| before version 2.00 in one or many documents, + there is \emph{no need} to change anything. Loading |csvsimple| + without options loads |csvsimple-legacy|. + |csvsimple-legacy| will be maintained to stay functional as it is for the + sake of compatibility to old documents. +\item Differences between |csvsimple-legacy| and |csvsimple-l3| are + discussed in \href{csvsimple.pdf}{\flqq The |csvsimple| package\frqq}. +\end{itemize} +\end{tcolorbox} + + +\clearpage +\tableofcontents + +\clearpage +\section{Introduction}% +The |csvsimple-legacy| package is applied to the processing of +CSV\footnote{CSV file: file with comma separated values.} files. +This processing is controlled by key value assignments according to the +syntax of |pgfkeys|. Sample applications of the package +are tabular lists, serial letters, and charts. + +An alternative to |csvsimple-legacy| is the |datatool| package +which provides considerably more functions and allows sorting of data by \LaTeX. +|csvsimple-legacy| has a different approach for the user interface and +is deliberately restricted to some basic functions with fast +processing speed. + +Mind the following restrictions: +\begin{itemize} +\item Sorting is not supported directly but can be done + with external tools, see \Fullref{sec:Sorting}. +\item Values are expected to be comma separated, but the package + provides support for other separators, see \Fullref{sec:separators}. +\item Values are expected to be either not quoted or quoted with + curly braces |{}| of \TeX\ groups. Other quotes like doublequotes + are not supported directly, but can be achieved + with external tools, see \Fullref{sec:importeddata}. +\item Every data line is expected to contain the same amount of values. + Unfeasible data lines are silently ignored by default, but this can + be configured, see \Fullref{sec:consistency}. +\end{itemize} + + +\subsection{Loading the Package} +The package |csvsimple-legacy| loads the packages +|pgfkeys|, +|etoolbox|, +and |ifthen|. +|csvsimple-legacy| itself is loaded with \emph{one} of the following +alternatives inside the preamble: +\begin{dispListing} +\usepackage{csvsimple} + % or alternatively (not simultaneously!) +\usepackage[legacy]{csvsimple} + % or alternatively (not simultaneously!) +\usepackage{csvsimple-legacy} +\end{dispListing} + + +Not automatically loaded, but used for many examples are the packages +|longtable| +and +|booktabs|. + + +\clearpage +\subsection{First Steps} +Every line of a processable CSV file has to contain an identical amount of +comma\footnote{See \refKey{/csv/separator} for other separators than comma.} separated values. The curly braces |{}| of \TeX\ groups can be used +to mask a block which may contain commas not to be processed as separators. + +The first line of such a CSV file is usually but not necessarily a header line +which contains the identifiers for each column. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{grade.csv} +name,givenname,matriculation,gender,grade +Maier,Hans,12345,m,1.0 +Huber,Anna,23456,f,2.3 +Weißbäck,Werner,34567,m,5.0 +Bauer,Maria,19202,f,3.3 +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{grade} + +\smallskip +The most simple way to display a CSV file in tabular form is the processing +with the \refCom{csvautotabular} command. + +\begin{dispExample} +\csvautotabular{grade.csv} +\end{dispExample} + + +Typically, one would use \refCom{csvreader} instead of |\csvautotabular| to +gain full control over the interpretation of the included data. + +In the following example, the entries of the header line are automatically +assigned to \TeX\ macros which may be used deliberately. + + +\begin{dispExample} +\begin{tabular}{|l|c|}\hline% +\bfseries Person & \bfseries Matr.~No. +\csvreader[head to column names]{grade.csv}{}% +{\\\givenname\ \name & \matriculation}% + \\\hline +\end{tabular} +\end{dispExample} + + +\clearpage +|\csvreader| is controlled by a plenty of options. For example, for table +applications line breaks are easily inserted by +\refKey{/csv/late after line}. This defines a macro execution just before +the following line. +Additionally, the assignment of columns to \TeX\ macros is shown in a non automated +way. + +\begin{dispExample} +\begin{tabular}{|r|l|c|}\hline% +& Person & Matr.~No.\\\hline\hline +\csvreader[late after line=\\\hline]% + {grade.csv}{name=\name,givenname=\firstname,matriculation=\matnumber}% + {\thecsvrow & \firstname~\name & \matnumber}% +\end{tabular} +\end{dispExample} + +\smallskip +An even more comfortable and preferrable way to create a table is setting +appropriate option keys. Note, that this gives you the possibility to create a +|pgfkeys| style which contains the whole table creation. + +\begin{dispExample} +\csvreader[tabular=|r|l|c|, + table head=\hline & Person & Matr.~No.\\\hline\hline, + late after line=\\\hline]% + {grade.csv}{name=\name,givenname=\firstname,matriculation=\matnumber}% + {\thecsvrow & \firstname~\name & \matnumber}% +\end{dispExample} + +\smallskip +The next example shows such a style definition with the convenience macro +\refCom{csvstyle}. Here, we see again the automated assignment of header +entries to column names by \refKey{/csv/head to column names}. +For this, the header entries have to be without spaces and special characters. +But you can always assign entries to canonical macro names by hand like in the examples +above. Here, we also add a \refKey{/csv/head to column names prefix} to avoid +macro name clashes. + +\begin{dispExample} +\csvstyle{myTableStyle}{tabular=|r|l|c|, + table head=\hline & Person & Matr.~No.\\\hline\hline, + late after line=\\\hline, + head to column names, + head to column names prefix=MY, + } + +\csvreader[myTableStyle]{grade.csv}{}% + {\thecsvrow & \MYgivenname~\MYname & \MYmatriculation}% +\end{dispExample} + + +\clearpage +Another way to address columns is to use their roman numbers. +The direct addressing is done by |\csvcoli|, |\csvcolii|, |\csvcoliii|, \ldots: + +\begin{dispExample} +\csvreader[tabular=|r|l|c|, + table head=\hline & Person & Matr.~No.\\\hline\hline, + late after line=\\\hline]% + {grade.csv}{}% + {\thecsvrow & \csvcolii~\csvcoli & \csvcoliii}% +\end{dispExample} + +\smallskip +And yet another method to assign macros to columns is to use arabic numbers +for the assignment: + +\begin{dispExample} +\csvreader[tabular=|r|l|c|, + table head=\hline & Person & Matr.~No.\\\hline\hline, + late after line=\\\hline]% + {grade.csv}{1=\name,2=\firstname,3=\matnumber}% + {\thecsvrow & \firstname~\name & \matnumber}% +\end{dispExample} + +\smallskip +For recurring applications, the |pgfkeys| syntax allows to create own styles +for a consistent and centralized design. The following example is easily +modified to obtain more or less option settings. + +\begin{dispExample} +\csvset{myStudentList/.style={% + tabular=|r|l|c|, + table head=\hline & Person & #1\\\hline\hline, + late after line=\\\hline, + column names={name=\name,givenname=\firstname} + }} + +\csvreader[myStudentList={Matr.~No.}]{grade.csv}{matriculation=\matnumber}% +{\thecsvrow & \firstname~\name & \matnumber}% +\hfill% +\csvreader[myStudentList={Grade}]{grade.csv}{grade=\grade}% +{\thecsvrow & \firstname~\name & \grade}% +\end{dispExample} + + +\clearpage +Alternatively, column names can be set by \refCom{csvnames} +and style definitions by \refCom{csvstyle}. +With this, the last example is rewritten as follows: + +\begin{dispExample} +\csvnames{myNames}{1=\name,2=\firstname,3=\matnumber,5=\grade} +\csvstyle{myStudentList}{tabular=|r|l|c|, + table head=\hline & Person & #1\\\hline\hline, + late after line=\\\hline, myNames} + +\csvreader[myStudentList={Matr.~No.}]{grade.csv}{}% +{\thecsvrow & \firstname~\name & \matnumber}% +\hfill% +\csvreader[myStudentList={Grade}]{grade.csv}{}% +{\thecsvrow & \firstname~\name & \grade}% +\end{dispExample} + +\smallskip +The data lines of a CSV file can also be filtered. In the following example, +a certificate is printed only for students with grade unequal to 5.0. + +\begin{dispExample} +\csvreader[filter not strcmp={\grade}{5.0}]% + {grade.csv}{1=\name,2=\firstname,3=\matnumber,4=\gender,5=\grade}% + {\begin{center}\Large\bfseries Certificate in Mathematics\end{center} + \large\ifcsvstrcmp{\gender}{f}{Ms.}{Mr.} + \firstname~\name, matriculation number \matnumber, has passed the test + in mathematics with grade \grade.\par\ldots\par + }% +\end{dispExample} + + +\clearpage +\section{Macros for the Processing of CSV Files}\label{sec:makros}% + +\begin{docCommand}{csvreader}{\oarg{options}\marg{file name}\marg{assignments}\marg{command list}} + |\csvreader| reads the file denoted by \meta{file name} line by line. + Every line of the file has to contain an identical amount of + comma separated values. The curly braces |{}| of \TeX\ groups can be used + to mask a block which may contain commas not to be processed as separators.\smallskip + + The first line of such a CSV file is by default but not necessarily + processed as a header line which contains the identifiers for each column. + The entries of this line can be used to give \meta{assignments} to \TeX\ macros + to address the columns. The number of entries of this first line + determines the accepted number of entries for all following lines. + Every line which contains a higher or lower number of entries is ignored + during standard processing.\smallskip + + The \meta{assignments} are given by key value pairs + \mbox{\meta{name}|=|\meta{macro}}. Here, \meta{name} is an entry from the + header line \emph{or} the arabic number of the addressed column. + \meta{macro} is some \TeX\ macro which gets the content of the addressed column.\smallskip + + The \meta{command list} is executed for every accepted data line. Inside the + \meta{command list} is applicable: + \begin{itemize} + \item \docAuxCommand{thecsvrow} or the counter |csvrow| which contains the number of the + current data line (starting with 1). + \item \docAuxCommand{csvcoli}, \docAuxCommand{csvcolii}, \docAuxCommand{csvcoliii}, \ldots, + which contain the contents of the column entries of the current data line. + Alternatively can be used: + \item \meta{macro} from the \meta{assignments} to have a logical + addressing of a column entry. + \end{itemize} + Note, that the \meta{command list} is allowed to contain |\par| and + that all macro definitions are made global to be used for table applications.\smallskip + + The processing of the given CSV file can be controlled by various + \meta{options} given as key value list. The feasible option keys + are described in section \ref{sec:schluessel} from page \pageref{sec:schluessel}. + +\begin{dispExample} +\csvreader[tabular=|r|l|l|, table head=\hline, table foot=\hline]{grade.csv}% + {name=\name,givenname=\firstname,grade=\grade}% + {\grade & \firstname~\name & \csvcoliii} +\end{dispExample} + +Mainly, the |\csvreader| command consists of a \refCom{csvloop} macro with +following parameters:\par +|\csvloop{|\meta{options}|, file=|\meta{file name}|, column names=|\meta{assignments}|,|\\ + \hspace*{2cm} |command=|\meta{command list}|}|\par + Therefore, the application of the keys \refKey{/csv/file} and \refKey{/csv/command} +is useless for |\csvreader|. +\end{docCommand} + +\begin{docCommand}{csvloop}{\marg{options}} + Usually, \refCom{csvreader} may be preferred instead of |\csvloop|. + \refCom{csvreader} is based on |\csvloop| which takes a mandatory list of + \meta{options} in key value syntax. + This list of \meta{options} controls the total processing. Especially, + it has to contain the CSV file name. +\begin{dispExample} +\csvloop{file={grade.csv}, head to column names, command=\name, + before reading={List of students:\ }, + late after line={{,}\ }, late after last line=.} +\end{dispExample} +\end{docCommand} + +\clearpage +The following |\csvauto...| commands are intended for quick data overview +with limited formatting potential. +See Subsection~\ref{subsec:tabsupport} on page \pageref{subsec:tabsupport} +for the general table options in combination with \refCom{csvreader} and +\refCom{csvloop}. + +\begin{docCommand}{csvautotabular}{\oarg{options}\marg{file name}} + |\csvautotabular| is an abbreviation for the application of the option key + \refKey{/csv/autotabular} together with other \meta{options} to \refCom{csvloop}. + This macro reads the whole CSV file denoted by \meta{file name} + with an automated formatting. +\begin{dispExample} +\csvautotabular{grade.csv} +\end{dispExample} +\begin{dispExample} +\csvautotabular[filter equal={\csvcoliv}{f}]{grade.csv} +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}{csvautolongtable}{\oarg{options}\marg{file name}} + |csvautolongtable| is an abbreviation for the application of the option key + \refKey{/csv/autolongtable} together with other \meta{options} to \refCom{csvloop}. + This macro reads the whole CSV file denoted by \meta{file name} + with an automated formatting. + For application, the package |longtable| is required which has to be + loaded in the preamble. +\begin{dispListing} +\csvautolongtable{grade.csv} +\end{dispListing} +\csvautolongtable{grade.csv} +\end{docCommand} + +\clearpage + +\begin{docCommand}{csvautobooktabular}{\oarg{options}\marg{file name}} + |\csvautobooktabular| is an abbreviation for the application of the option key + \refKey{/csv/autobooktabular} together with other \meta{options} to \refCom{csvloop}. + This macro reads the whole CSV file denoted by \meta{file name} + with an automated formatting. + For application, the package |booktabs| is required which has to be + loaded in the preamble. +\begin{dispExample} +\csvautobooktabular{grade.csv} +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}{csvautobooklongtable}{\oarg{options}\marg{file name}} + |csvautobooklongtable| is an abbreviation for the application of the option key + \refKey{/csv/autobooklongtable} together with other \meta{options} to \refCom{csvloop}. + This macro reads the whole CSV file denoted by \meta{file name} + with an automated formatting. + For application, the packages |booktabs| and |longtable| are required which have to be + loaded in the preamble. +\begin{dispListing} +\csvautobooklongtable{grade.csv} +\end{dispListing} +\csvautobooklongtable{grade.csv} +\end{docCommand} + + + +\clearpage + +\begin{docCommand}{csvset}{\marg{options}} + Sets \meta{options} for every following + \refCom{csvreader} and \refCom{csvloop}. For example, this command may + be used for style definitions. +\begin{dispExample} +\csvset{grade list/.style= + {column names={name=\name,givenname=\firstname,grade=\grade}}, + passed/.style={filter not strcmp={\grade}{5.0}} } + +The following students passed the test in mathematics: +\csvreader[grade list,passed]{grade.csv}{}{\firstname\ \name\ (\grade); }% +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}{csvstyle}{\marg{key}\marg{options}} + Abbreviation for |\csvset{|\meta{key}|/.style=|\marg{options}|}| + to define a new style. +\end{docCommand} + +\begin{docCommand}{csvnames}{\marg{key}\marg{assignments}} + Abbreviation for |\csvset{|\meta{key}|/.style={column names=|\marg{assignments}|}}| + to define additional \meta{assignments} of macros to columns. +\begin{dispExample} +\csvnames{grade list}{name=\name,givenname=\firstname,grade=\grade} +\csvstyle{passed}{filter not strcmp={\grade}{5.0}} + +The following students passed the test in mathematics: +\csvreader[grade list,passed]{grade.csv}{}{\firstname\ \name\ (\grade); }% +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}{csvheadset}{\marg{assignments}} + For some special cases, this command can be used to change the + \meta{assignments} of macros to columns during execution of + \refCom{csvreader} and \refCom{csvloop}. +\begin{dispExample} +\csvreader{grade.csv}{}% + { \csvheadset{name=\n} \fbox{\n} + \csvheadset{givenname=\n} \ldots\ \fbox{\n} }% +\end{dispExample} +\end{docCommand} + +\clearpage + +\begin{docCommand}{csviffirstrow}{\marg{then macros}\marg{else macros}} + Inside the command list of \refCom{csvreader}, the \meta{then macros} + are executed for the first data line, and the \meta{else macros} + are executed for all following lines. +\begin{dispExample} +\csvreader[tabbing, head to column names, table head=\hspace*{3cm}\=\kill]% + {grade.csv}{}% + {\givenname~\name \> (\csviffirstrow{first entry!!}{following entry})} +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}{csvifoddrow}{\marg{then macros}\marg{else macros}} + Inside the command list of \refCom{csvreader}, the \meta{then macros} + are executed for odd-numbered data lines, and the \meta{else macros} + are executed for even-numbered lines. +\begin{dispExample} +\csvreader[head to column names,tabular=|l|l|l|l|, + table head=\hline\bfseries \# & \bfseries Name & \bfseries Grade\\\hline, + table foot=\hline]{grade.csv}{}{% + \csvifoddrow{\slshape\thecsvrow & \slshape\name, \givenname & \slshape\grade}% + {\bfseries\thecsvrow & \bfseries\name, \givenname & \bfseries\grade}} +\end{dispExample} + +The |\csvifoddrow| macro may be used for striped tables: + +\begin{dispExample} +% This example needs the xcolor package +\csvreader[head to column names,tabular=rlcc, + table head=\hline\rowcolor{red!50!black}\color{white}\# & \color{white}Person + & \color{white}Matr.~No. & \color{white}Grade, + late after head=\\\hline\rowcolor{yellow!50}, + late after line=\csvifoddrow{\\\rowcolor{yellow!50}}{\\\rowcolor{red!25}}]% + {grade.csv}{}% + {\thecsvrow & \givenname~\name & \matriculation & \grade}% +\end{dispExample} + +\enlargethispage*{1cm} +Alternatively, |\rowcolors| from the |xcolor| package can be used for this +purpose: + +\begin{dispExample} +% This example needs the xcolor package +\csvreader[tabular=rlcc, before table=\rowcolors{2}{red!25}{yellow!50}, + table head=\hline\rowcolor{red!50!black}\color{white}\# & \color{white}Person + & \color{white}Matr.~No. & \color{white}Grade\\\hline, + head to column names]{grade.csv}{}% + {\thecsvrow & \givenname~\name & \matriculation & \grade}% +\end{dispExample} +\end{docCommand} + +\clearpage + +\begin{docCommand}{csvfilteraccept}{} + All following consistent data lines will be accepted and processed. + This command overwrites all previous filter settings and may be used + inside \refKey{/csv/full filter} to implement + an own filtering rule together with |\csvfilterreject|. +\begin{dispExample} +\csvreader[autotabular, + full filter=\ifcsvstrcmp{\csvcoliv}{m}{\csvfilteraccept}{\csvfilterreject} + ]{grade.csv}{}{\csvlinetotablerow}% +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}{csvfilterreject}{} + All following data lines will be ignored. + This command overwrites all previous filter settings. +\end{docCommand} + + +\begin{docCommand}{csvline}{} + This macro contains the current and unprocessed data line. +\begin{dispExample} +\csvreader[no head, tabbing, table head=\textit{line XX:}\=\kill]% + {grade.csv}{}{\textit{line \thecsvrow:} \> \csvline}% +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}{thecsvrow}{} + Typesets the current data line number. This is the + current number of accepted data lines without the header line. + The \LaTeX\ counter |csvrow| can be addressed directly in the usual way, + e.\,g. by |\roman{csvrow}|. +\end{docCommand} + + +\begin{docCommand}{thecsvinputline}{} + Typesets the current file line number. This is the + current number of all data lines including the header line. + The \LaTeX\ counter |csvinputline| can be addressed directly in the usual way, + e.\,g. by |\roman{csvinputline}|. +\begin{dispExample} +\csvreader[no head, filter test=\ifnumequal{\thecsvinputline}{3}]% + {grade.csv}{}% + {The line with number \thecsvinputline\ contains: \csvline}% +\end{dispExample} +\end{docCommand} + + +\begin{docCommand}[doc updated=2016-07-01]{csvlinetotablerow}{} + Typesets the current processed data line with |&| between the entries. + %Most users will never apply this command. +\end{docCommand} + + + +\clearpage +\section{Option Keys}\label{sec:schluessel}% +For the \meta{options} in \refCom{csvreader} respectively \refCom{csvloop} +the following |pgf| keys can be applied. The key tree path |/csv/| is not +to be used inside these macros. + + +\subsection{Command Definition}%--------%[[ + +\begin{docCsvKey}{before reading}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed before the CSV file is processed. +\end{docCsvKey} + +\begin{docCsvKey}{after head}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after the header line is read. +\end{docCsvKey} + +\begin{docCsvKey}{before filter}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after reading and consistency checking + of a data line. They are executed before any filter condition is checked, + see \refKey{/csv/filter}. + Also see \refKey{/csv/full filter}. +\end{docCsvKey} + +\begin{docCsvKey}{late after head}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after reading and disassembling + of the first accepted data line. They are executed before further processing + of this line. +\end{docCsvKey} + +\begin{docCsvKey}{late after line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after reading and disassembling + of the next accepted data line (after \refKey{/csv/before filter}). + They are executed before further processing of this next line. + |late after line| overwrites |late after first line| and |late after last line|. + Note that table options like \refKey{/csv/tabular} set this key to |\\| + automatically. +\end{docCsvKey} + + +\begin{docCsvKey}{late after first line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after reading and disassembling + of the second accepted data line instead of \refKey{/csv/late after line}. + This key has to be set after |late after line|. +\end{docCsvKey} + + +\begin{docCsvKey}{late after last line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after processing of the last + accepted data line instead of \refKey{/csv/late after line}. + This key has to be set after |late after line|. +\end{docCsvKey} + + +\begin{docCsvKey}{before line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after \refKey{/csv/late after line} + and before \refKey{/csv/command}. + |before line| overwrites |before first line|. +\end{docCsvKey} + + +\begin{docCsvKey}{before first line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed instead of \refKey{/csv/before line} + for the first accepted data line. + This key has to be set after |before line|. +\end{docCsvKey} + + +\begin{docCsvKey}{command}{=\meta{code}}{no default, initially \cs{csvline}} + Sets the \meta{code} to be executed for every accepted data line. + They are executed between \refKey{/csv/before line} and \refKey{/csv/after line}. +\end{docCsvKey} + + +\begin{docCsvKey}{after line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed for every accepted data line + after \refKey{/csv/command}. + |after line| overwrites |after first line|. +\end{docCsvKey} + + +\begin{docCsvKey}{after first line}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed instead of \refKey{/csv/after line} + for the first accepted data line. + This key has to be set after |after line|. +\end{docCsvKey} + +\begin{docCsvKey}{after reading}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after the CSV file is processed. +\end{docCsvKey} + + +\begin{dispExample} +\csvreader[ + before reading = \meta{before reading}\\, + after head = \meta{after head}, + before filter = \\\meta{before filter}, + late after head = \meta{late after head}, + late after line = \meta{late after line}, + late after first line = \meta{late after first line}, + late after last line = \\\meta{late after last line}, + before line = \meta{before line}, + before first line = \meta{before first line}, + after line = \meta{after line}, + after first line = \meta{after first line}, + after reading = \\\meta{after reading} + ]{grade.csv}{name=\name}{\textbf{\name}}% +\end{dispExample} + +Additional command definition keys are provided for the supported tables, +see Section~\ref{subsec:tabsupport} from page~\pageref{subsec:tabsupport}. + +\clearpage +\subsection{Header Processing and Column Name Assignment}% + +\begin{docCsvKey}{head}{\colOpt{=true\textbar false}}{default |true|, initially |true|} + If this key is set, the first line of the CSV file is treated as a header + line which can be used for column name assignments. +\end{docCsvKey} + + +\begin{docCsvKey}{no head}{}{no value} + Abbreviation for |head=false|, i.\,e. the first line of the CSV file is + treated as data line. + Note that this option cannot be used in combination with + \refCom{csvautotabular}, \refKey{/csv/autotabular}, and similar automated commands/options. + See Section~\ref{noheader} on page~\pageref{noheader} for assistance. +\end{docCsvKey} + +\begin{docCsvKey}{column names}{=\meta{assignments}}{no default, initially empty} + Adds some new \meta{assignments} of macros to columns in key value syntax. + Existing assignments are kept. +\end{docCsvKey} + + +\begin{docCsvKey}{column names reset}{}{no value} + Clears all assignments of macros to columns. +\end{docCsvKey} + + +\begin{docCsvKey}{head to column names}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, the entries of the header line are used automatically + as macro names for the columns. This option can be used only, if + the header entries do not contain spaces and special characters to be + used as feasible \LaTeX\ macro names. + Note that the macro definition is \emph{global} and may therefore override + existing macros for the rest of the document. Adding + \refKey{/csv/head to column names prefix} may help to avoid unwanted + overrides. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2019-07-16]{head to column names prefix}{=\meta{text}}{no default, initially empty} + The given \meta{text} is prefixed to the name of all macros generated by + \refKey{/csv/head to column names}. For example, if you use the settings +\begin{dispListing} + head to column names, + head to column names prefix=MY, +\end{dispListing} + a header entry |section| will generate the corresponding macro + |\MYsection| instead of destroying the standard \LaTeX\ |\section| macro. +\end{docCsvKey} + + +\clearpage +\subsection{Consistency Check}\label{sec:consistency}% + +\begin{docCsvKey}{check column count}{\colOpt{=true\textbar false}}{default |true|, initially |true|} + This key defines, wether the number of entries in a data line is checked against + an expected value or not.\\ + If |true|, every non consistent line is ignored without announcement.\\ + If |false|, every line is accepted and may produce an error during + further processing. +\end{docCsvKey} + + +\begin{docCsvKey}{no check column count}{}{no value} + Abbreviation for |check column count=false|. +\end{docCsvKey} + + +\begin{docCsvKey}{column count}{=\meta{number}}{no default} + Sets the \meta{number} of feasible entries per data line. + This setting is only useful in connection with \refKey{/csv/no head}, + since \meta{number} would be replaced by the number of entries in the + header line otherwise. +\end{docCsvKey} + + +\begin{docCsvKey}{on column count error}{=\meta{code}}{no default, initially empty} + \meta{code} to be executed for unfeasible data lines. +\end{docCsvKey} + + +\begin{docCsvKey}{warn on column count error}{}{style, no value} + Display of a warning for unfeasible data lines. +\end{docCsvKey} + + +\clearpage +\subsection{Filtering}% + +\begin{docCsvKey}[][doc new=2016-07-01]{filter test}{=\meta{condition}}{no default} + Only data lines which fulfill a logical \meta{condition} are accepted. + For the \meta{condition}, every single test normally employed like +\begin{dispListing} +\iftest{some testing}{true}{false} +\end{dispListing} + can be used as +\begin{dispListing} +filter test=\iftest{some testing}, +\end{dispListing} + For |\iftest|, tests from the |etoolbox| package like + |\ifnumcomp|, |\ifdimgreater|, etc. and from \Fullref{sec:stringtests} can be used. + +\begin{dispExample} +\csvreader[head to column names,tabular=llll, + table head=\toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot=\bottomrule, + %>> list only matriculation numbers greater than 20000 << + filter test=\ifnumgreater{\matriculation}{20000}, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade} +\end{dispExample} +\end{docCsvKey} + + +\begin{docCsvKey}{filter strcmp}{=\marg{stringA}\marg{stringB}}{style, no default} + Only lines where \meta{stringA} and \meta{stringB} are equal after expansion + are accepted. + The implementation is done with \refCom{ifcsvstrcmp}. +\end{docCsvKey} + + +\begin{docCsvKey}{filter not strcmp}{=\marg{stringA}\marg{stringB}}{style, no default} + Only lines where \meta{stringA} and \meta{stringB} are not equal after expansion + are accepted. + The implementation is done with \refCom{ifcsvnotstrcmp}. +\end{docCsvKey} + + +\begin{docCsvKey}[][doc new=2016-07-01]{filter expr}{=\meta{condition}}{no default} + Only data lines which fulfill a logical \meta{condition} are accepted. + For the \meta{condition}, every boolean expression + from the |etoolbox| package is feasible. + To preprocess the data line before testing the \meta{condition}, + the option key \refKey{/csv/before filter} can be used. +\begin{dispExample} +\csvreader[head to column names,tabular=llll, + table head=\toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot=\bottomrule, + %>> list only matriculation numbers greater than 20000 + % and grade less than 4.0 << + filter expr={ test{\ifnumgreater{\matriculation}{20000}} + and test{\ifdimless{\grade pt}{4.0pt}} }, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade} +\end{dispExample} +\end{docCsvKey} + +\clearpage +\begin{docCsvKey}[][doc new=2016-07-01]{filter ifthen}{=\meta{condition}}{no default} + Only data lines which fulfill a logical \meta{condition} are accepted. + For the \meta{condition}, every term from the |ifthen| package + is feasible. + To preprocess the data line before testing the \meta{condition}, + the option key \refKey{/csv/before filter} can be used. + +\begin{dispExample} +\csvreader[head to column names,tabular=llll, + table head=\toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot=\bottomrule, + %>> list only female persons << + filter ifthen=\equal{\gender}{f}, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade} +\end{dispExample} + +\end{docCsvKey} + + +\begin{docCsvKey}{filter}{=\meta{condition}}{no default} + Alias for \refKey{/csv/filter ifthen}. +\end{docCsvKey} + +\begin{docCsvKey}{filter equal}{=\marg{stringA}\marg{stringB}}{style, no default} + Only lines where \meta{stringA} and \meta{stringB} are equal after expansion + are accepted. + The implementation is done with the |ifthen| package. +\end{docCsvKey} + + +\begin{docCsvKey}{filter not equal}{=\marg{stringA}\marg{stringB}}{style, no default} + Only lines where \meta{stringA} and \meta{stringB} are not equal after expansion + are accepted. + The implementation is done with the |ifthen| package. +\end{docCsvKey} + + + +\begin{docCsvKey}{no filter}{}{no value, initially set} + Clears a set filter. +\end{docCsvKey} + + +\begin{docCsvKey}{filter accept all}{}{no value, initially set} + Alias for |no filter|. All consistent data lines are accepted. +\end{docCsvKey} + + +\begin{docCsvKey}{filter reject all}{}{no value} + All data line are ignored. +\end{docCsvKey} + + + +\enlargethispage*{2cm} +\begin{docCsvKey}[][doc new=2016-07-01]{full filter}{=\meta{code}}{no default} + Technically, this key is an alias for \refKey{/csv/before filter}. + Philosophically, \refKey{/csv/before filter} computes something before + a filter condition is set, but \refKey{/csv/full filter} should implement + the full filtering. Especially, \refCom{csvfilteraccept} or + \refCom{csvfilterreject} \emph{should} be set inside the \meta{code}. +\begin{dispExample} +\csvreader[head to column names,tabular=llll, + table head=\toprule & \bfseries Name & \bfseries Matr & \bfseries Grade\\\midrule, + table foot=\bottomrule, + %>> list only matriculation numbers greater than 20000 + % and grade less than 4.0 << + full filter=\ifnumgreater{\matriculation}{20000} + {\ifdimless{\grade pt}{4.0pt}{\csvfilteraccept}{\csvfilterreject}} + {\csvfilterreject}, + ]{grade.csv}{}{% + \thecsvrow & \slshape\name, \givenname & \matriculation & \grade} +\end{dispExample} +\end{docCsvKey} + + + +%]] + + +\clearpage +\subsection{Table Support}\label{subsec:tabsupport}%--------%[[ + +\begin{docCsvKey}{tabular}{=\meta{table format}}{style, no default} + Surrounds the CSV processing with |\begin{tabular}|\marg{table format} + at begin and with |\end{tabular}| at end. +Additionally, the commands defined by the key values of + \refKey{/csv/before table}, \refKey{/csv/table head}, \refKey{/csv/table foot}, + and \refKey{/csv/after table} are executed at the appropriate places. +\end{docCsvKey} + + +\begin{docCsvKey}{centered tabular}{=\meta{table format}}{style, no default} + Like \refKey{/csv/tabular} but inside an additional |center| environment. +\end{docCsvKey} + + +\begin{docCsvKey}{longtable}{=\meta{table format}}{style, no default} + Like \refKey{/csv/tabular} but for the |longtable| environment. + This requires the package |longtable| (not loaded automatically). +\end{docCsvKey} + + +\begin{docCsvKey}{tabbing}{}{style, no value} + Like \refKey{/csv/tabular} but for the |tabbing| environment. +\end{docCsvKey} + + +\begin{docCsvKey}{centered tabbing}{}{style, no value} + Like \refKey{/csv/tabbing} but inside an additional |center| environment. +\end{docCsvKey} + + +\begin{docCsvKey}{no table}{}{style, no value} + Deactivates |tabular|, |longtable|, and |tabbing|. +\end{docCsvKey} + + +\begin{docCsvKey}{before table}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed before |\begin{tabular}| or before |\begin{longtable}| + or before |\begin{tabbing}|, respectively. +\end{docCsvKey} + + +\begin{docCsvKey}{table head}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after |\begin{tabular}| or after |\begin{longtable}| + or after |\begin{tabbing}|, respectively. +\end{docCsvKey} + + +\begin{docCsvKey}{table foot}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed before |\end{tabular}| or before |\end{longtable}| + or before |\end{tabbing}|, respectively. +\end{docCsvKey} + + +\begin{docCsvKey}{after table}{=\meta{code}}{no default, initially empty} + Sets the \meta{code} to be executed after |\end{tabular}| or after |\end{longtable}| + or after |\end{tabbing}|, respectively. +\end{docCsvKey} + +\bigskip + +The following |auto| options are the counterparts for the respective quick +overview commands like \refCom{csvautotabular}. They are listed for +completeness, but are unlikely to be used directly. + +\begin{docCsvKey}{autotabular}{=\meta{file name}}{no default} + Reads the whole CSV file denoted \meta{file name} with an automated formatting. +\end{docCsvKey} + + +\begin{docCsvKey}{autolongtable}{=\meta{file name}}{no default} + Reads the whole CSV file denoted \meta{file name} with an automated formatting + using the required |longtable| package. +\end{docCsvKey} + + +\begin{docCsvKey}{autobooktabular}{=\meta{file name}}{no default} + Reads the whole CSV file denoted \meta{file name} with an automated formatting + using the required |booktabs| package. +\end{docCsvKey} + + +\begin{docCsvKey}{autobooklongtable}{=\meta{file name}}{no default} + Reads the whole CSV file denoted \meta{file name} with an automated formatting + using the required |booktabs| and |longtable| packages. +\end{docCsvKey} + + +\clearpage +\subsection{Special Characters}\label{subsec:specchar} +Be default, the CSV content is treated like normal \LaTeX\ text, see +Subsection~\ref{macrocodexample} on page~\pageref{macrocodexample}. +But, \TeX\ special characters of the CSV content may also be interpreted +as normal characters, if one or more of the following options are used. + +\begin{docCsvKey}{respect tab}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + tabulator sign + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect percent}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + percent sign \verbbox{\%} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect sharp}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + sharp sign \verbbox{\#} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect dollar}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + dollar sign \verbbox{\$} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect and}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + and sign \verbbox{\&} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect backslash}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + backslash sign \verbbox{\textbackslash} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect underscore}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + underscore sign \verbbox{\_} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect tilde}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + tilde sign \verbbox{\textasciitilde} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect circumflex}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + circumflex sign \verbbox{\textasciicircum} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect leftbrace}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + left brace sign \verbbox{\textbraceleft} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect rightbrace}{\colOpt{=true\textbar false}}{default |true|, initially |false|} + If this key is set, every + right brace sign \verbbox{\textbraceright} + inside the CSV content is a normal character. +\end{docCsvKey} + +\begin{docCsvKey}{respect all}{}{style, no value, initially unset} + Set all special characters from above to normal characters. This means + a quite verbatim interpretation of the CSV content. +\end{docCsvKey} + +\begin{docCsvKey}{respect none}{}{style, no value, initially set} + Do not change any special character from above to normal character. +\end{docCsvKey} + +\clearpage +\subsection{Separators}\label{sec:separators}% +\begin{docCsvKey}{separator}{=\meta{sign}}{no default, initially |comma|} + \catcode `|=12 + Sets the \meta{sign} which is treates as separator between the data values + of a data line. Feasible values are: + \begin{itemize} + \item\docValue{comma}: This is the initial value with '\texttt{,}' as separator. + \medskip + + \item\docValue{semicolon}: Sets the separator to '\texttt{;}'. +\begin{dispExample} +% \usepackage{tcolorbox} for tcbverbatimwrite +\begin{tcbverbatimwrite}{testsemi.csv} + name;givenname;matriculation;gender;grade + Maier;Hans;12345;m;1.0 + Huber;Anna;23456;f;2.3 + Weißbäck;Werner;34567;m;5.0 +\end{tcbverbatimwrite} + +\csvautobooktabular[separator=semicolon]{testsemi.csv} +\end{dispExample} +\medskip + +\item\docValue{pipe}: Sets the separator to '\texttt{|}'. +\begin{dispExample} +% \usepackage{tcolorbox} for tcbverbatimwrite +\begin{tcbverbatimwrite}{pipe.csv} + name|givenname|matriculation|gender|grade + Maier|Hans|12345|m|1.0 + Huber|Anna|23456|f|2.3 + Weißbäck|Werner|34567|m|5.0 +\end{tcbverbatimwrite} + +\csvautobooktabular[separator=pipe]{pipe.csv} +\end{dispExample} +\medskip + +\item\docValue{tab}: Sets the separator to the tabulator sign. + Automatically, \refKey{/csv/respect tab} is set also. + \end{itemize} +\end{docCsvKey} + +\clearpage +\subsection{Miscellaneous}% + +\begin{docCsvKey}{every csv}{}{style, initially empty} + A style definition which is used for every following CSV file. + This definition can be overwritten with user code. +\begin{dispListing} +% Sets a warning message for unfeasible data lines. +\csvset{every csv/.style={warn on column count error}} +% Alternatively: +\csvstyle{every csv}{warn on column count error} +\end{dispListing} +\end{docCsvKey} + +\begin{docCsvKey}{default}{}{style} + A style definition which is used for every following CSV file which + resets all settings to default values\footnote{\texttt{default} is used + because of the global nature of most settings.}. + This key should not be used or changed by the user if there is not a + really good reason (and you know what you do). +\end{docCsvKey} + + +\begin{docCsvKey}{file}{=\meta{file name}}{no default, initially |unknown.csv|} + Sets the \meta{file name} of the CSV file to be processed. +\end{docCsvKey} + + +\begin{docCsvKey}{preprocessed file}{=\meta{file name}}{no default, initially \texttt{\textbackslash\detokenize{jobname_sorted.csv}}} + Sets the \meta{file name} of the CSV file which is the output of a + preprocessor. +\end{docCsvKey} + + +\begin{docCsvKey}{preprocessor}{=\meta{macro}}{no default} + Defines a preprocessor for the given CSV file. + The \meta{macro} has to have two mandatory arguments. The first argument + is the original CSV file which is set by \refKey{/csv/file}. + The second argument is the preprocessed CSV file + which is set by \refKey{/csv/preprocessed file}.\par\smallskip + Typically, the \meta{macro} may call an external program which preprocesses + the original CSV file (e.\,g. sorting the file) and creates the + preprocessed CSV file. The later file is used by \refCom{csvreader} + or \refCom{csvloop}. +\begin{dispListing} +\newcommand{\mySortTool}[2]{% + % call to an external program to sort file #1 with resulting file #2 +} + +\csvreader[% + preprocessed file=\jobname_sorted.csv, + preprocessor=\mySortTool, + ]{some.csv}{}{% + % do something +} +\end{dispListing} +See Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting} for a +concrete sorting preprocessing implemented with an external tool. +\end{docCsvKey} + + +\begin{docCsvKey}{no preprocessing}{}{style, no value, initially set} + Clears any preprocessing, i.\,e. preprocessing is switched of. +\end{docCsvKey} + + + +\clearpage +\subsection{Sorting}\label{sec:Sorting}% +\TeX/\LaTeX\ was not born under a sorting planet. |csvsimple-legacy| provides no +sorting of data lines by \LaTeX-methods since sorting can be done much faster +and much better by external tools. + +First, one should consider the appropriate \emph{place} for sorting: +\begin{itemize} +\item CSV files may be sorted by a tool \emph{before} the \LaTeX\ document is processed + at all. If the CSV data is not likely to change, this is the most efficient method. +\item CSV files may be sorted by a tool every time before the \LaTeX\ document is compiled. + This could be automated by a shell script or some processing tool like |arara|. +\item CSV files may be sorted on-the-fly by a tool during compilation of + a \LaTeX\ document. This is the most elegant but not the most efficient way. +\end{itemize} + +The first two methods are decoupled from anything concerning |csvsimple-legacy|. +For the third method, the \refKey{/csv/preprocessor} option is made for. +This allows to access an external tool for sorting. +\emph{Which tool} is your choice. + +\csvsorter\ was written as a companion tool for |csvsimple|. +It is an open source Java command-line tool for sorting CSV files, available at\\ +\url{http://T-F-S.github.io/csvsorter/}\quad or\quad +\url{https://github.com/T-F-S/csvsorter} + +It can be +used for all three sorting approaches described above. +There is special support for on-the-fly sorting with \csvsorter\ using the +following options. + +\begin{enumerate}\bfseries +\item To use the sorting options, you have to install \csvsorter\ before!\\ + |csvsimple| v1.12 or newer needs \csvsorter\ v0.94 of newer! +\item You have to give permission to call external tools during + compilation, i.\,e.\ the command-line options for |latex| have to include + |-shell-escape|. +\end{enumerate} + +\bigskip + +\begin{docCsvKey}{csvsorter command}{=\meta{system command}}{no default, initially |csvsorter|} + The \meta{system command} specifies the system call for \csvsorter\ (without the options). + If \csvsorter\ was completely installed following its documentation, there is + nothing to change here. If the |csvsorter.jar| file is inside the same + directory as the \LaTeX\ source file, you may configure:% preferrably inside the preamble: +\begin{dispListing} +\csvset{csvsorter command=java -jar csvsorter.jar} +\end{dispListing} +\end{docCsvKey} + +\begin{docCsvKey}{csvsorter configpath}{=\meta{path}}{no default, initially |.|} + Sorting with \csvsorter\ is done using XML configuration files. If these files + are not stored inside the same directory as the \LaTeX\ source file, a + \meta{path} to access them can be configured: +\begin{dispListing} +\csvset{csvsorter configpath=xmlfiles} +\end{dispListing} + Here, the configuration files would be stored in a subdirectory named |xmlfiles|. +\end{docCsvKey} + +\begin{docCsvKey}{csvsorter log}{=\meta{file name}}{no default, initially |csvsorter.log|} + Sets the log file of \csvsorter\ to the given \meta{file name}. +\begin{dispListing} +\csvset{csvsorter log=outdir/csvsorter.log} +\end{dispListing} + Here, the log file is written to a subdirectory named |outdir|. +\end{docCsvKey} + +\clearpage +\begin{docCsvKey}{csvsorter token}{=\meta{file name}}{no default, initially |\textbackslash jobname.csvtoken|} + Sets \meta{file name} as token file. This is an auxiliary file which + communicates the success of \csvsorter\ to |csvsimple|. +\begin{dispListing} +\csvset{csvsorter log=outdir/\jobname.csvtoken} +\end{dispListing} + Here, the token file is written to a subdirectory named |outdir|. +\end{docCsvKey} + + +\begin{docCsvKey}{sort by}{=\meta{file name}}{style, initially unset} + The \meta{file name} denotes an XML configuration file for \csvsorter. + Setting this option inside \refCom{csvreader} or + \refCom{csvloop} will issue a system call to \csvsorter. + \begin{itemize} + \item \csvsorter\ uses the given CSV file as input file. + \item \csvsorter\ uses \meta{file name} as configuration file. + \item The output CSV file is denoted by \refKey{/csv/preprocessed file} + which is by default \texttt{\textbackslash\detokenize{jobname_sorted.csv}}. + This output file is this actual file processed by \refCom{csvreader} or \refCom{csvloop}. + \item \csvsorter\ also generates a log file denoted by \refKey{/csv/csvsorter log} which is by default |csvsorter.log|. + \end{itemize} + +\par\medskip\textbf{First example:} + To sort our example |grade.csv| file according to |name| and |givenname|, we + use the following XML configuration file. Since \csvsorter\ uses double quotes + as default brackets for column values, we remove bracket recognition to avoid + a clash with the escaped umlauts of the example CSV file.\par\smallskip + +\xmllisting{namesort} +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[sort by=namesort.xml, + head to column names, + tabular=>{\color{red}}lllll, + table head=\toprule Name & Given Name & Matriculation & Gender & Grade\\\midrule, + table foot=\bottomrule] + {grade.csv}{}{\csvlinetotablerow} +\end{dispExample} + +\clearpage\textbf{Second example:} + To sort our example |grade.csv| file according to |grade|, we + use the following XML configuration file. Further, persons with the same |grade| + are sorted by |name| and |givenname|. Since \csvsorter\ uses double quotes + as default brackets for column values, we remove bracket recognition to avoid + a clash with the escaped umlauts of the example CSV file.\par\smallskip + +\xmllisting{gradesort} +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[sort by=gradesort.xml, + head to column names, + tabular=llll>{\color{red}}l, + table head=\toprule Name & Given Name & Matriculation & Gender & Grade\\\midrule, + table foot=\bottomrule] + {grade.csv}{}{\csvlinetotablerow} +\end{dispExample} + +\clearpage\textbf{Third example:} + To generate a matriculation/grade list, we sort our example |grade.csv| file + using the following XML configuration file. + Again, since \csvsorter\ uses double quotes + as default brackets for column values, we remove bracket recognition to avoid + a clash with the escaped umlauts of the example CSV file.\par\smallskip + +\xmllisting{matriculationsort} +\begin{dispExample} +% \usepackage{booktabs} +\csvreader[sort by=matriculationsort.xml, + head to column names, + tabular=>{\color{red}}ll, + table head=\toprule Matriculation & Grade\\\midrule, + table foot=\bottomrule] + {grade.csv}{}{\matriculation & \grade} +\end{dispExample} +\end{docCsvKey} + + +\clearpage +\begin{docCsvKey}{new sorting rule}{=\marg{name}\marg{file name}}{style, initially unset} +This is a convenience option to generate a new shortcut for often used +\refKey{/csv/sort by} applications. It also adds a more semantic touch. +The new shortcut option is +\tcbox[on line,size=small,colback=white,colframe=red]{|sort by| \meta{name}} which expands to +\tcbox[on line,size=small,colback=white,colframe=red]{|sort by=|\marg{file name}}.\par\medskip + +Consider the following example: +\begin{dispExample} +\csvautotabular[sort by=namesort.xml]{grade.csv} +\end{dispExample} +A good place for setting up a new sorting rule would be inside the preamble: + +\csvset{new sorting rule={name}{namesort.xml}} +\begin{dispListing} +\csvset{new sorting rule={name}{namesort.xml}} +\end{dispListing} + +Now, we can use the new rule: +\begin{dispExample} +\csvautotabular[sort by name]{grade.csv} +\end{dispExample} + +\end{docCsvKey} + + +\clearpage +\section{String Tests}\label{sec:stringtests}% + +The following string tests are complementing the string tests +from the |etoolbox| package. They all do the same, i.e., +comparing expanded strings for equality. +\begin{itemize} +\item\refCom{ifcsvstrcmp} is the most efficient method, because it uses + native compiler string comparison (if available). +\item\refCom{ifcsvstrequal} does not rely on a compiler. It also is the + fallback implementation for \refCom{ifcsvstrcmp}, if there is no + native comparison method. +\item\refCom{ifcsvprostrequal} is possibly more failsafe than the other two + string tests. It may be used, if strings contain dirty things like |\textbf{A}|. +\end{itemize} +\medskip + +\begin{docCommand}[doc new=2016-07-01]{ifcsvstrcmp}{\marg{stringA}\marg{stringB}\marg{true}\marg{false}} + Compares two strings and executes \meta{true} if they are equal, and \meta{false} otherwise. + The comparison is done using |\pdfstrcmp|, if compilation is done with pdf\LaTeX. + The comparison is done using |\pdf@strcmp|, if the package |pdftexcmds| is + loaded and compilation is done with lua\LaTeX\ or Xe\LaTeX. + Otherwise, \refCom{ifcsvstrcmp} is identical to \refCom{ifcsvstrequal}. + This command cannot be used inside the preamble. +\end{docCommand} + + +\begin{docCommand}[doc new=2016-07-01]{ifcsvnotstrcmp}{\marg{stringA}\marg{stringB}\marg{true}\marg{false}} + Compares two strings and executes \meta{true} if they are \emph{not} equal, and \meta{false} otherwise. + The implementation uses \refCom{ifcsvstrcmp}. +\end{docCommand} + + +\begin{docCommand}[doc new=2016-07-01]{ifcsvstrequal}{\marg{stringA}\marg{stringB}\marg{true}\marg{false}} + Compares two strings and executes \meta{true} if they are equal, and \meta{false} otherwise. + The strings are expanded with |\edef| in the test. +\end{docCommand} + +\begin{docCommand}[doc new=2016-07-01]{ifcsvprostrequal}{\marg{stringA}\marg{stringB}\marg{true}\marg{false}} + Compares two strings and executes \meta{true} if they are equal, and \meta{false} otherwise. + The strings are expanded with |\protected@edef| in the test, i.e. parts of the + strings which are protected stay unexpanded. +\end{docCommand} + + + +\clearpage +\section{Examples}% + +\subsection{A Serial Letter}% +In this example, a serial letter is to be written to all persons with +addresses from the following CSV file. Deliberately, the file content is +not given in very pretty format. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{address.csv} +name,givenname,gender,degree,street,zip,location,bonus +Maier,Hans,m,,Am Bachweg 17,10010,Hopfingen,20 + % next line with a comma in curly braces +Huber,Erna,f,Dr.,{Moosstraße 32, Hinterschlag},10020,Örtingstetten,30 +Weißbäck,Werner,m,Prof. Dr.,Brauallee 10,10030,Klingenbach,40 + % this line is ignored % + Siebener , Franz,m, , Blaumeisenweg 12 , 10040 , Pardauz , 50 + % preceding and trailing spaces in entries are removed % +Schmitt,Anton,m,,{\AE{}lfred-Esplanade, T\ae{}g 37}, 10050,\OE{}resung,60 +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{address} + +Firstly, we survey the file content quickly using +|\csvautotabular|. +As can be seen, unfeasible lines are ignored automatically. + +\begin{dispExample} +\tiny\csvautotabular{address.csv} +\end{dispExample} + +Now, we create the serial letter where every feasible data line produces +an own page. Here, we simulate the page by a |tcolorbox| (from the package +|tcolorbox|). +For the gender specific salutations, an auxiliary macro |\ifmale| is +introduced. + +\begin{dispExample} +% this example requires the tcolorbox package +\newcommand{\ifmale}[2]{\ifcsvstrcmp{\gender}{m}{#1}{#2}} + +\csvreader[head to column names]{address.csv}{}{% +\begin{tcolorbox}[colframe=DarkGray,colback=White,arc=0mm,width=(\linewidth-2pt)/2, + equal height group=letter,before=,after=\hfill,fonttitle=\bfseries, + adjusted title={Letter to \name}] + \ifcsvstrcmp{\degree}{}{\ifmale{Mr.}{Ms.}}{\degree}~\givenname~\name\\ + \street\\\zip~\location + \tcblower + {\itshape Dear \ifmale{Sir}{Madam},}\\ + we are pleased to announce you a bonus value of \bonus\%{} + which will be delivered to \location\ soon.\\\ldots +\end{tcolorbox}} +\end{dispExample} + + + +\clearpage +\subsection{A Graphical Presentation}% +For this example, we use some artificial statistical data given by a CSV file. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{data.csv} +land,group,amount +Bayern,A,1700 +Baden-Württemberg,A,2300 +Sachsen,B,1520 +Thüringen,A,1900 +Hessen,B,2100 +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{data} + +Firstly, we survey the file content using +|\csvautobooktabular|. + +\begin{dispExample} +% needs the booktabs package +\csvautobooktabular{data.csv} +\end{dispExample} + +The amount values are presented in the following diagram by bars where +the group classification is given using different colors. + +\begin{dispExample} +% This example requires the package tikz +\begin{tikzpicture}[Group/A/.style={left color=red!10,right color=red!20}, + Group/B/.style={left color=blue!10,right color=blue!20}] +\csvreader[head to column names]{data.csv}{}{% + \begin{scope}[yshift=-\thecsvrow cm] + \path [draw,Group/\group] (0,-0.45) + rectangle node[font=\bfseries] {\amount} (\amount/1000,0.45); + \node[left] at (0,0) {\land}; + \end{scope} } +\end{tikzpicture} +\end{dispExample} + + +\clearpage +It would be nice to sort the bars by length, i.\,e.\ to sort the CSV file +by the |amount| column. If the \csvsorter\ program is properly installed, +see Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting}, +this can be done with the following configuration file for \csvsorter: + +\xmllisting{amountsort} + +Now, we just have to add an option |sort by=amountsort.xml|: +\begin{dispExample} +% This example requires the package tikz +% Also, the CSV-Sorter tool has to be installed +\begin{tikzpicture}[Group/A/.style={left color=red!10,right color=red!20}, + Group/B/.style={left color=blue!10,right color=blue!20}] +\csvreader[head to column names,sort by=amountsort.xml]{data.csv}{}{% + \begin{scope}[yshift=-\thecsvrow cm] + \path [draw,Group/\group] (0,-0.45) + rectangle node[font=\bfseries] {\amount} (\amount/1000,0.45); + \node[left] at (0,0) {\land}; + \end{scope} } +\end{tikzpicture} +\end{dispExample} + + + + +\clearpage +Next, we create a pie chart by calling |\csvreader| twice. +In the first step, the total sum of amounts is computed, and in the second +step the slices are drawn. + +\begin{dispExample} +% Modified example from www.texample.net for pie charts +% This example needs the packages tikz, xcolor, calc +\definecolorseries{myseries}{rgb}{step}[rgb]{.95,.85,.55}{.17,.47,.37} +\resetcolorseries{myseries}% + +% a pie slice +\newcommand{\slice}[4]{ + \pgfmathsetmacro{\midangle}{0.5*#1+0.5*#2} + \begin{scope} + \clip (0,0) -- (#1:1) arc (#1:#2:1) -- cycle; + \colorlet{SliceColor}{myseries!!+}% + \fill[inner color=SliceColor!30,outer color=SliceColor!60] (0,0) circle (1cm); + \end{scope} + \draw[thick] (0,0) -- (#1:1) arc (#1:#2:1) -- cycle; + \node[label=\midangle:#4] at (\midangle:1) {}; + \pgfmathsetmacro{\temp}{min((#2-#1-10)/110*(-0.3),0)} + \pgfmathsetmacro{\innerpos}{max(\temp,-0.5) + 0.8} + \node at (\midangle:\innerpos) {#3}; +} + +% sum of amounts +\csvreader[before reading=\def\mysum{0}]{data.csv}{amount=\amount}{% + \pgfmathsetmacro{\mysum}{\mysum+\amount}% +} + +% drawing of the pie chart +\begin{tikzpicture}[scale=3]% +\def\mya{0}\def\myb{0} +\csvreader[head to column names]{data.csv}{}{% + \let\mya\myb + \pgfmathsetmacro{\myb}{\myb+\amount} + \slice{\mya/\mysum*360}{\myb/\mysum*360}{\amount}{\land} +} +\end{tikzpicture}% +\end{dispExample} + + +\clearpage +Finally, the filter option is demonstrated by separating the groups A and B. +Every item is piled upon the appropriate stack. + +\begin{dispExample} +\newcommand{\drawGroup}[2]{% + \def\mya{0}\def\myb{0} + \node[below=3mm] at (2.5,0) {\bfseries Group #1}; + \csvreader[head to column names,filter equal={\group}{#1}]{data.csv}{}{% + \let\mya\myb + \pgfmathsetmacro{\myb}{\myb+\amount} + \path[draw,top color=#2!25,bottom color=#2!50] + (0,\mya/1000) rectangle node{\land\ (\amount)} (5,\myb/1000); +}} + +\begin{tikzpicture} + \fill[gray!75] (-1,0) rectangle (13,-0.1); + \drawGroup{A}{red} + \begin{scope}[xshift=7cm] + \drawGroup{B}{blue} + \end{scope} +\end{tikzpicture} + +\end{dispExample} + + +\clearpage +\subsection{Macro code inside the data}\label{macrocodexample}% + +If needed, the data file may contain macro code. Note that the first character +of a data line is not allowed to be the backslash '|\|'. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{macrodata.csv} +type,description,content +M,A nice \textbf{formula}, $\displaystyle \int\frac{1}{x} = \ln|x|+c$ +G,A \textcolor{red}{colored} ball, {\tikz \shadedraw [shading=ball] (0,0) circle (.5cm);} +M,\textbf{Another} formula, $\displaystyle \lim\limits_{n\to\infty} \frac{1}{n}=0$ +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{macrodata} + +Firstly, we survey the file content using +|\csvautobooktabular|. + +\begin{dispExample} +\csvautobooktabular{macrodata.csv} +\end{dispExample} + + +\begin{dispExample} +\csvstyle{my enumerate}{head to column names, + before reading=\begin{enumerate},after reading=\end{enumerate}} + +\csvreader[my enumerate]{macrodata.csv}{}{% + \item \description:\par\content} + +\bigskip +Now, formulas only: +\csvreader[my enumerate,filter equal={\type}{M}]{macrodata.csv}{}{% + \item \description:\qquad\content} +\end{dispExample} + +\clearpage +\subsection{Tables with Number Formatting}\label{numberformatting}% + +We consider a file with numerical data which should be pretty-printed. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{data_numbers.csv} +month, dogs, cats +January, 12.50,12.3e5 +February, 3.32, 8.7e3 +March, 43, 3.1e6 +April, 0.33, 21.2e4 +May, 5.12, 3.45e6 +June, 6.44, 6.66e6 +July, 123.2,7.3e7 +August, 12.3, 5.3e4 +September,2.3, 4.4e4 +October, 6.5, 6.5e6 +November, 0.55, 5.5e5 +December, 2.2, 3.3e3 +\end{tcbverbatimwrite} + +\csvlisting{data_numbers} + +The |siunitx| package provides a new column type |S| +which can align material using a number of different strategies. +The following example demonstrates the application with CSV reading. +The package documentation of |siunitx| contains a huge amount +of formatting options. + +\begin{dispExample} +% \usepackage{siunitx,array,booktabs} +\csvloop{ + file=data_numbers.csv, + head to column names, + before reading=\centering\sisetup{table-number-alignment=center}, + tabular={lSS[table-format=2.2e1]@{}c}, + table head=\toprule\textbf{Month} & \textbf{Dogs} & \textbf{Cats} &\\\midrule, + command=\month & \dogs & \cats &, + table foot=\bottomrule} +\end{dispExample} + +\clearpage +Special care is needed, if the \emph{first} or the \emph{last} column is to be formatted with +the column type |S|. The number detection of |siunitx| is disturbed by +the line reading code of |csvsimple-legacy| which actually is present at the +first and last column. To avoid this problem, the content of the first and last column +could be formatted not by the table format definition, but by using a +suitable |\tablenum| formatting directly, see |siunitx|. + +Another and very nifty workaround suggested by Enrico Gregorio is to +add an invisible dummy column with |c@{}| as first column +and |@{}c| as last column: + + +\begin{dispExample} +% \usepackage{siunitx,array,booktabs} +\csvloop{ + file=data_numbers.csv, + head to column names, + before reading=\centering\sisetup{table-number-alignment=center}, + tabular={c@{}S[table-format=2.2e1]S@{}c}, + table head= & \textbf{Cats} & \textbf{Dogs} & \\\midrule, + command= & \cats & \dogs &, + table foot=\bottomrule} +\end{dispExample} + + +\clearpage +Now, the preceding table shall be sorted by the \emph{cats} values. +If the \csvsorter\ program is properly installed, +see Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting}, +this can be done with the following configuration file for \csvsorter: + +\xmllisting{catsort} + +Now, we just have to add an option |sort by=catsort.xml|: +\begin{dispExample} +% \usepackage{siunitx,array,booktabs} +% Also, the CSV-Sorter tool has to be installed +\csvloop{ + file=data_numbers.csv, + sort by=catsort.xml, + head to column names, + before reading=\centering\sisetup{table-number-alignment=center}, + tabular={lSS[table-format=2.2e1]@{}c}, + table head=\toprule\textbf{Month} & \textbf{Dogs} & \textbf{Cats} & \\\midrule, + command=\month & \dogs & \cats &, + table foot=\bottomrule} +\end{dispExample} + + +\clearpage +\subsection{CSV data without header line}\label{noheader}% +CSV files with a header line are more semantic than files without header, +but it's no problem to work with headless files. + +For this example, we use again some artificial statistical data given by a CSV file +but this time without header. + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{data_headless.csv} +Bayern,A,1700 +Baden-Württemberg,A,2300 +Sachsen,B,1520 +Thüringen,A,1900 +Hessen,B,2100 +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{data_headless} + +Note that you cannot use the \refKey{/csv/no head} option for the auto tabular +commands. If no options are given, the first line is interpreted as header line +which gives an unpleasant result: + +\begin{dispExample} +\csvautobooktabular{data_headless.csv} +\end{dispExample} + +To get the expected result, one can redefine \refKey{/csv/table head} +using \refCom{csvlinetotablerow} which holds the first line data for the +|\csvauto...| commands: + +\begin{dispExample} +\csvautobooktabular[table head=\toprule\csvlinetotablerow\\]{data_headless.csv} +\end{dispExample} + +This example can be extended to insert a table head for this headless data: + +\begin{dispExample} +\csvautobooktabular[table head=\toprule\bfseries Land & \bfseries Group + & \bfseries Amount\\\midrule\csvlinetotablerow\\]{data_headless.csv} +\end{dispExample} + +\clearpage + +For the normal \refCom{csvreader} command, the \refKey{/csv/no head} option +should be applied. Of course, we cannot use \refKey{/csv/head to column names} +because there is no head, but the columns can be addressed by their numbers: + +\begin{dispExample} +\csvreader[no head, + tabular=lr, + table head=\toprule\bfseries Land & \bfseries Amount\\\midrule, + table foot=\bottomrule] + {data_headless.csv} + {1=\land,3=\amount} + {\land & \amount} +\end{dispExample} + + +\clearpage +\subsection{Imported CSV data}\label{sec:importeddata}% +If data is imported from other applications, there is not always a choice +to format in comma separated values with curly brackets. + +Consider the following example data file: + +%-- file embedded for simplicity -- +\begin{tcbverbatimwrite}{imported.csv} +"name";"address";"email" +"Frank Smith";"Yellow Road 123, Brimblsby";"frank.smith@organization.org" +"Mary May";"Blue Alley 2a, London";"mmay@maybe.uk" +"Hans Meier";"Hauptstraße 32, Berlin";"hans.meier@corporation.de" +\end{tcbverbatimwrite} +%-- end embedded file -- + +\csvlisting{imported} + +If the \csvsorter\ program is properly installed, +see Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting}, +this can be transformed on-the-fly +with the following configuration file for \csvsorter: + +\xmllisting{transform} + +Now, we just have to add an option |sort by=transform.xml| to transform +the input data. Here, we actually do not sort. + +\begin{dispExample} +% \usepackage{booktabs,array} +% Also, the CSV-Sorter tool has to be installed +\newcommand{\Header}[1]{\normalfont\bfseries #1} + +\csvreader[ + sort by=transform.xml, + tabular=>{\itshape}ll>{\ttfamily}l, + table head=\toprule\Header{Name} & \Header{Address} & \Header{email}\\\midrule, + table foot=\bottomrule] + {imported.csv}{}{\csvlinetotablerow} +\end{dispExample} + +The file which is generated on-the-fly and which is actually read by +|csvsimple-legacy| is the following: + +\tcbinputlisting{docexample,listing style=tcbdocumentation,fonttitle=\bfseries, + listing only,listing file=\jobname_sorted._csv} + + +\clearpage +\subsection{Encoding}\label{encoding}% +If the CSV file has a different encoding than the \LaTeX\ source file, +then special care is needed. + +\begin{itemize} +\item The most obvious treatment is to change the encoding of the CSV file + or the \LaTeX\ source file to match the other one (every good editor + supports such a conversion). This is the easiest choice, if there a no + good reasons against such a step. E.g., unfortunately, several tools + under Windows need the CSV file to be |cp1252| encoded while + the \LaTeX\ source file may need to be |utf8| encoded. + +\item The |inputenc| package allows to switch the encoding inside the + document, say from |utf8| to |cp1252|. Just be aware that you should only + use pure ASCII for additional texts inside the switched region. +\begin{dispListing} +% !TeX encoding=UTF-8 +% .... +\usepackage[utf8]{inputenc} +% .... +\begin{document} +% .... +\inputencoding{latin1}% only use ASCII from here, e.g. "Uberschrift +\csvreader[%... + ]{data_cp1252.csv}{%... + }{% .... + } +\inputencoding{utf8} +% .... +\end{document} +\end{dispListing} + +\item As a variant to the last method, the encoding switch can be done + using options from |csvsimple-legacy|: +\begin{dispListing} +% !TeX encoding=UTF-8 +% .... +\usepackage[utf8]{inputenc} +% .... +\begin{document} +% .... +% only use ASCII from here, e.g. "Uberschrift +\csvreader[%... + before reading=\inputencoding{latin1}, + after reading=\inputencoding{utf8}, + ]{data_cp1252.csv}{%... + }{% .... + } +% .... +\end{document} +\end{dispListing} + +\pagebreak\item +If the \csvsorter\ program is properly installed, +see Subsection~\ref{sec:Sorting} on page~\pageref{sec:Sorting}, +the CSV file can be re-encoded on-the-fly +with the following configuration file for \csvsorter: + +\xmllisting{encoding} + +\begin{dispListing} +% !TeX encoding=UTF-8 +% .... +\usepackage[utf8]{inputenc} +% .... +\begin{document} +% .... +\csvreader[%... + sort by=encoding.xml, + ]{data_cp1252.csv}{%... + }{% .... + } +% .... +\end{document} +\end{dispListing} + + +\end{itemize} + + +\clearpage + +\printindex + +\end{document} diff --git a/csvsimple/csvsimple-title.png b/csvsimple/csvsimple-title.png new file mode 100644 index 0000000..0a574bd Binary files /dev/null and b/csvsimple/csvsimple-title.png differ diff --git a/csvsimple/csvsimple.pdf b/csvsimple/csvsimple.pdf new file mode 100644 index 0000000..e4b5bad Binary files /dev/null and b/csvsimple/csvsimple.pdf differ diff --git a/csvsimple/csvsimple.sty b/csvsimple/csvsimple.sty new file mode 100644 index 0000000..1d3840f --- /dev/null +++ b/csvsimple/csvsimple.sty @@ -0,0 +1,49 @@ +%% The LaTeX package csvsimple - version 2.5.0 (2023/10/16) +%% csvsimple.sty: Simple LaTeX CSV file processing +%% +%% ------------------------------------------------------------------------------------------- +%% Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm +%% ------------------------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `author-maintained'. +%% +%% This work consists of all files listed in README.md +%% +\RequirePackage{l3keys2e} + +\ProvidesExplPackage{csvsimple}{2023/10/16}{2.5.0} + {LaTeX CSV file processing} + +\cs_if_exist:NT \c__csvsim_package_expl_bool + { + \msg_new:nnn { csvsimple }{ package-loaded } + { Package~'#1'~seems~already~be~loaded! } + \bool_if:NTF \c__csvsim_package_expl_bool + { + \msg_warning:nn { csvsimple }{ package-loaded }{ csvsimple-l3 } + } + { + \msg_warning:nn { csvsimple }{ package-loaded }{ csvsimple-legacy } + } + \tex_endinput:D + } + +\keys_define:nn { csvsimple } + { + l3 .code:n = \tl_set:Nn \l__csvsim_package_expl_tl { l3 }, + legacy .code:n = \tl_set:Nn \l__csvsim_package_expl_tl { legacy }, + } + +\keys_set:nn { csvsimple } { legacy } + +\ProcessKeysPackageOptions { csvsimple } + +\RequirePackage{csvsimple-\l__csvsim_package_expl_tl} diff --git a/csvsimple/csvsimple.tex b/csvsimple/csvsimple.tex new file mode 100644 index 0000000..7e4e93d --- /dev/null +++ b/csvsimple/csvsimple.tex @@ -0,0 +1,175 @@ +% \LaTeX-Main\ +% !TeX encoding=UTF-8 +%% The LaTeX package csvsimple - version 2.5.0 (2023/10/16) +%% csvsimple.tex: Manual +%% +%% ------------------------------------------------------------------------------------------- +%% Copyright (c) 2008-2023 by Prof. Dr. Dr. Thomas F. Sturm +%% ------------------------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `author-maintained'. +%% +%% This work consists of all files listed in README.md +%% +\documentclass[a4paper,11pt]{ltxdoc} +\usepackage{csvsimple-doc} + +\usepackage{\csvpkgprefix csvsimple-legacy} + +\hypersetup{ + pdftitle={Manual for the csvsimple package}, + pdfauthor={Thomas F. Sturm}, + pdfsubject={csv file processing with LaTeX}, + pdfkeywords={csv file, comma separated values, key value syntax} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\begin{document} +\begin{center} +\begin{tcolorbox}[enhanced,hbox,tikznode,left=8mm,right=8mm,boxrule=0.4pt, + colback=white,colframe=black!50!yellow, + drop lifted shadow=black!50!yellow,arc is angular, + before=\par\vspace*{5mm},after=\par\bigskip] +{\bfseries\LARGE The \texttt{csvsimple} package}\\[3mm] +{\large Manual for version \version\ (\datum)} +\end{tcolorbox} +{\large Thomas F.~Sturm% + \footnote{Prof.~Dr.~Dr.~Thomas F.~Sturm, Institut f\"{u}r Mathematik und Informatik, + University of the Bundeswehr Munich, D-85577 Neubiberg, Germany; + email: \href{mailto:thomas.sturm@unibw.de}{thomas.sturm@unibw.de}}\par\medskip +\normalsize\url{https://www.ctan.org/pkg/csvsimple}\par +\url{https://github.com/T-F-S/csvsimple} +} +\end{center} +\bigskip +\begin{absquote} + \begin{center}\bfseries Abstract\end{center} + |csvsimple| provides a simple \LaTeX\ interface for the processing of files with + comma separated values (CSV). |csvsimple| relies heavily on a key value + syntax which results in an easy way of usage. + Filtering and table generation is especially supported. Since the package + is considered as a lightweight tool, there is no support for data sorting + or data base storage. +\end{absquote} + +\section{Package Options}% + +|csvsimple| is a stub which merely selects to load exclusively one of the +following packages: +\begin{itemize} +\item \href{csvsimple-l3.pdf}{\flqq The |csvsimple-l3| package\frqq}:\\ + This is the pure \LaTeX3 version of |csvsimple|. It is considered + to be the \emph{current} version. + New documents are encouraged to use this package.\par + |csvsimple-l3| is loaded with \emph{one} of the following + alternatives inside the preamble: +\begin{dispListing} +\usepackage[l3]{csvsimple} + % or alternatively (not simultaneously!) +\usepackage{csvsimple-l3} +\end{dispListing} +\medskip + +\item \href{csvsimple-legacy.pdf}{\flqq The |csvsimple-legacy| package\frqq}:\\ + This is the \LaTeXe{} version of |csvsimple|. It is considered + to be the \emph{superseded} version identical to version 1.22 of |csvsimple|. + Documents based on that former version do \emph{not have to be changed} + and stay compilable in future.\par + |csvsimple-legacy| is loaded with \emph{one} of the following + alternatives inside the preamble: +\begin{dispListing} +\usepackage{csvsimple} + % or alternatively (not simultaneously!) +\usepackage[legacy]{csvsimple} + % or alternatively (not simultaneously!) +\usepackage{csvsimple-legacy} +\end{dispListing} +\end{itemize} + + +\clearpage +\section{Differences between \texttt{csvsimple-l3} and \texttt{csvsimple-legacy}} +This section is intended for users who know |csvsimple| before version~2.00. + +|csvsimple-l3| is a \emph{nearly} drop-in replacement for +|csvsimple-legacy|. Although old documents have no \emph{need} to be changed, +adopting the new \LaTeX3 version for existing documents should impose not +too much effort. Actually, it depends on how intense |pgfkeys| specific +styles were used. + +That brings us to the differences between the two packages and a more precise +understanding what \emph{nearly} drop-in replacement means. The following enumeration +does not list new features of \texttt{csvsimple-l3} (if any), but takes an +upgrade point of view. + +\begin{itemize} +\item Any patches or additions using undocumented internals of |csvsimple-legacy| + will stop to function, because |csvsimple-l3| has a completely implementation. +\item |csvsimple-l3| is programmed in |expl3| code using the \LaTeX3 interfaces. + No additional packages are loaded or needed with exception of several options + which allow to access methods from |ifthen|, |etoolbox|, |longtable|, etc. + On the other hand, |csvsimple-legacy| is programmed in \LaTeXe{} with + dirty tricks from here and there. +\item The most significant change of the user interface is that the key value + engine of |csvsimple-legacy| is |pgfkeys| (root \docAuxKey*[csv]{}) while |csvsimple-l3| uses + |l3keys| (root \docAuxKey*[csvsim]{}). + Names and usage of the keys are \emph{unchanged}. + But, if + you made own |pgfkeys| \emph{styles} using the |pgfkeys| style handler, + these \emph{styles} have to be adapted to |.meta| keys of |l3keys|. + The good news is that styles + made with \docAuxCommand*{csvstyle} become |.meta| keys automatically. +\item The macro \docAuxCommand*{csvheadset} is removed. It is not supportable + by the new implementation. I never used it and I forgot why I ever wrote it + -- I hope the same is true for you. If not, |csvsimple-legacy| can be + used for documents which needs it. +\item Option \docAuxKey*[csv]{filter} is removed. Instead, \docAuxKey*[csvsim]{filter ifthen} + can be used (also true with \docAuxKey*[csv]{filter ifthen} for the old version). +\item The deprecated options + \docAuxKey*[csv]{nofilter} and \docAuxKey*[csv]{nohead} are removed. + They were not documented any more since years. Obviously, use + \docAuxKey*[csvsim]{no filter} and \docAuxKey*[csvsim]{no head} instead. +\item Compilation problems are to be expected, if an |S| column of the |siunitx| package + is used as first or last column. Documents neglecting this rule successfully + for |csvsimple-legacy|, may fail to compile with |csvsimple-l3.| +\item The \LaTeX{} counters \docCounter*{csvinputline} + and \docCounter*{csvrow} + are replaced by \LaTeX3 integers + \docCounter*{g_csvsim_inputline_int} + and \docCounter*{g_csvsim_row_int}, but accessors + \docAuxCommand*{thecsvinputline} and + \docAuxCommand*{thecsvrow} are still valid. +\item The packages |pgfrcs|, |pgfkeys|, |ifthen|, |etoolbox|, and |shellesc| + are not included anymore (include manually, if needed). +\item + \docAuxCommand*{csviffirstrow} and + \docAuxCommand*{csvifoddrow} are deprecated and replaced by + \docAuxCommand*{ifcsvfirstrow} + \docAuxCommand*{ifcsvoddrow} + which are more consistent in nomenclature. +\item For |csvsimple-l3|, data lines are allowed to begin with an backslash. +\item Assigned macros like |\myname| for e.g. the third column contain + not |\csvcoliii| anymore, but are equal to the content of |\csvcoliii| now. +\item Character code changes with \docAuxKey*[csvsim]{respect percent} etc. + and the tabulator as separator should work for |csvsimple-l3| as expected in every + situation (not always worked for |csvsimple-legacy|). +\item A drawback of |csvsimple-l3| against |csvsimple-legacy| is + a higher compilation time. This may vary by used compiler. + An example document of 5061 pages using a CSV file with 166 992 lines + took about 28 seconds with |csvsimple-legacy| and + about 51 seconds with |csvsimple-l3| on my machine + (just a singular observation, no scientific analysis at all). +\end{itemize} + + + + +\end{document} diff --git a/csvsimple/encoding.xml b/csvsimple/encoding.xml new file mode 100644 index 0000000..e43cb6a --- /dev/null +++ b/csvsimple/encoding.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/csvsimple/gradesort.xml b/csvsimple/gradesort.xml new file mode 100644 index 0000000..32c2741 --- /dev/null +++ b/csvsimple/gradesort.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/csvsimple/matriculationsort.xml b/csvsimple/matriculationsort.xml new file mode 100644 index 0000000..be19e9e --- /dev/null +++ b/csvsimple/matriculationsort.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/csvsimple/namesort.xml b/csvsimple/namesort.xml new file mode 100644 index 0000000..6c17bd4 --- /dev/null +++ b/csvsimple/namesort.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/csvsimple/transform.xml b/csvsimple/transform.xml new file mode 100644 index 0000000..4111c31 --- /dev/null +++ b/csvsimple/transform.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/document.tex b/document.tex new file mode 100644 index 0000000..e30c8f9 --- /dev/null +++ b/document.tex @@ -0,0 +1,114 @@ + + +\documentclass[fontsize=12pt]{scrlttr2} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{% + ngerman, + ae, + times, %% hier kann man die Schriftart einstellen + graphicx, + url} +\usepackage{eurosym} +\usepackage{pdfpages} +\usepackage{nopageno} +\usepackage{array} +\usepackage{makecell} +\usepackage{csvsimple} + +% Hier die Optionen fuer scrlttr2 eintragen. +% S.a. scrguide.pdf ab Seite 150 +\KOMAoptions{paper=a4,fromalign=center,fromrule=aftername, +backaddress=true,parskip=half,enlargefirstpage=true} + +% Falls man das alte scrlettr vemisst, oder die Option +% "enlargefirstpage=true" aus den KOMAoptions +% benutzen will, muss man den folgenden Eintrag +% auskommentieren (einfach das %-Zeichen löschen). +%\LoadLetterOption{KOMAold} + +% hier Name und darunter Anschrift einsetzen: +\setkomavar{fromname}{CCL} +\setkomavar{fromaddress}{Oberhafen\\ + Hamburg} + +% hier die Signatur einsetzen: +\setkomavar{signature}{Sebastian Hinz} + +% hier betreff einsetzen +\setkomavar{subject}{Spendenquittung} + +% hier kommt dein Ort hin: +\setkomavar{place}{Hamburg} + +% die Signatur ist linksbuendig +\let\raggedsignature=\raggedright + +% Manche finden, dass scrlttr2 so eine riesige Fußzeile hat +% einfach die nächste Zeile auskommentieren, dann wird sie kleiner: +% \setlength{\footskip}{-6pt} + +% pdflatex \"\\newcommand{\\vorname}{#{vorname}} +% \\newcommand{\\nachname}{#{name}} +% \\newcommand{\\strasse}{#{strasse + " " + hausnummer.to_s}} +% \\newcommand{\\plz}{#{plz}} +% \\newcommand{\\ort}{#{ort}} +% \\newcommand{\\betrag}{#{betrag}} +% \\newcommand{\\betraginworten}{#{betrag_in_worten}} +% \\newcommand{\\startdatum}{#{start_datum}} +% \\newcommand{\\findatum}{#{end_datum}} +% \\newcommand{\\mitgliedsnummer} + +\begin{document} + % die Anschrift des Empfaengers + \begin{letter}{\vorname{} \nachname{} \\ + \strasse{}\\ + \plz{} \ort{}} + +\opening{Sehr geehrte Damen und Herren,} + +Mitgliedsnummer: \mitgliedsnummer{}\\ +Betrag: \betrag{} \euro{}\\ +Betrag in worten: \betraginworten{}\\ +Zeitraum: \startdatum{} - \findatum{}\\ + +%\csvautotabular{payments.csv} + +\csvreader[ + tabular=|l|l|l|l|, + table head=\hline \thead{ Datum der Zuwendung} & \thead{Art der Zuwendung \\ (Geldzuwendung/Mitgliedsbeitrag)} & \thead{Verzicht auf die Erstattung\\ von Aufwendungen (ja/nein)} & Betrag\\\hline, + late after line=\\\hline, +]{payments.csv}{datum=\datum,betrag=\betrag,type=\type,verzicht=\verzicht}% +{\datum & \type & \verzicht &\betrag \euro{}}% +% {\csvcoli & \csvcolii} + +\closing{Mit freundlichen Grüßen} + + + +\end{letter} +\end{document} + + + + + + + + + + + + + + + + + + + + + + + +