Repository navigation
Fix handling of SAS formats and informats in sas7bdat files - #419
Conversation
|
@evanmiller Do you have reservations about merging this PR? I can see multiple possibilities:
BackgroundThis PR is motivated by the discussion in the pyreadstat issue to enable the ReadStat write API for sas7bdat files: Roche/pyreadstat#205 Previously, it was more of a cosmetic issue that ReadStat didn't follow SAS conventions when reporting formats: The more relevant problem is that the SAS7BDAT writer currently accepts a non-SAS format name like With this PR, the writer accepts both |
Review of #419The overall direction looks sound: always splitting the format on write, rejecting invalid format strings, and reporting Blocker: files written by earlier ReadStat versions become unreadableUp to now, including v1.1.9 and current if (strchr(dst, '.') || (width == 0 && digits != 0))
return READSTAT_ERROR_PARSE;rejects those names. Because the error propagates, the whole file fails to parse, not just that column's format. Test: a one-column file written by the
Any sas7bdat written through haven or pyreadstat with an ordinary SAS format string like Suggested fixMake the reader lenient for this legacy layout: if the stored name contains a A test covering this would also help, e.g. a small fixture file written by the current writer with a Other notes
|
The writer only split a format into name, width and digits when the string had no trailing dot, so "YYMMDD10." was written verbatim with width 0. It now always splits via the XPT format parser and rejects invalid format strings with READSTAT_ERROR_BAD_FORMAT_STRING. The reader now always rebuilds the format with a dot (NAME., NAMEw., NAMEw.d) and returns a parse error for digits without a width. Also allow 32-character names in xport_format_t.
538635a to
ba72a85
Compare
|
Thanks for the detailled feedback! Much appreciated! I've made the reader more lenient as requested, and rebased on I've not added the requested test fixture as the project previously has been quite judicious about adding static files to the test framework. And I think the lean testing approach based on round-trips has been spectacularly validated by the very minor fixes that are now required to fully build out the sas7bdat write feature after the breakthrough of #398. |
|
Thanks! |
Problem
In sas7bdat files, a format such as
DOLLAR12.2is stored in two places:DOLLARin the column text subheader12and digits2in the column subheaderThe writer kept the split only if the rebuilt string matched the input exactly. Formats with a trailing dot, like
YYMMDD10., never matched, so the whole string was written as the name with width 0. The reader left out the dot whenever digits were 0 (DOLLAR12instead ofDOLLAR12.), and it didn't check the stored data.This is intended as a follow-up on #398, which unlocked the writing of SAS data files readable by SAS. The setup of the sas7bdat writer for formats and informats too easily produced SAS data files unreadable by SAS.
Changes
Writer (
readstat_sas7bdat_write.c)READSTAT_ERROR_BAD_FORMAT_STRING. They used to be written as the name without any error, producing SAS data files unreadable by SAS.Reader (
readstat_sas7bdat_read.c)NAME.,NAMEw.orNAMEw.d. An empty name gives the plainw.dformat (e.g.10.3).READSTAT_ERROR_PARSEif the stored name contains., of if width is 0 and digit isn't.Shared (
readstat_xport.h)xport_format_t.namegrows to 33 bytes so that names of 32 characters, the SAS maximum, fit with their terminator.Behavior changes
$CHAR8.instead of$CHAR8.