You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Calculate XPT row_count using metadata and final chunk #315
For XPT files, I understand that row_count cannot be extracted from the metadata alone, but I think it can be calculated using only the metadata and final 80-byte chunk.
Expected behavior
Read the header information to find: variable_sizes and the start of record data
Calculate record_size as sum of variable_sizes
Read the last 80-byte chunk of data to find out how much trailing ASCII blank padding there is.
Calculate number of records using: (total_file_size - start - padding) / record_size
It would be helpful if readstat could expose either:
Hi Gerry. Not proposing a solution, but I faced the same problem when implementing XPT v5 parser in JS. I calculated it from metadata using very similar approach to what you described above (here is my solution for reference https://github.com/defineEditor/xport-js/blob/c8da602428b25c94187befb20928e35a48d518ae/src/classes/member.ts#L162)
Maybe the only difference that in my case I had to calculate records_start_offset manually and instead of subtracting a padding, I just used floor function.
Mainly I just want to confirm that this approach works and gives a correct row_count at least for XPTv5, so I hope it will be implemented in ReadStat at some point.
Note that the the last record of the xport file for a dataset is padded with blanks to fill the 80 blocks used by xport files. When the data record length is less than the 80 bytes then it is possible for there to be room for extra records in that padding. In those cases SAS will ignore data records that are all blanks at the end of the last block of the file when reading the file.
One way to detect these is to decrement your calculated number of observations for every all blank data record detected at the end of the last block.
if obslen<80 and nobs>0 then do;
j=mod(nobs*obslen,80);
if j=0 then j=80;
do j=j+1-obslen to 1 by -obslen;
if substr(lastrec,j,obslen) ne ' ' then leave;
else nobs=nobs-1;
end;
end;
Activity
DmitryMK commentedon Mar 10, 2025
Hi Gerry. Not proposing a solution, but I faced the same problem when implementing XPT v5 parser in JS. I calculated it from metadata using very similar approach to what you described above (here is my solution for reference
https://github.com/defineEditor/xport-js/blob/c8da602428b25c94187befb20928e35a48d518ae/src/classes/member.ts#L162)
Maybe the only difference that in my case I had to calculate records_start_offset manually and instead of subtracting a padding, I just used floor function.
Mainly I just want to confirm that this approach works and gives a correct row_count at least for XPTv5, so I hope it will be implemented in ReadStat at some point.
evanmiller commentedon May 23, 2025
For XPT v8 we should be able to just read in the observation count described here: #322
sasutils commentedon Mar 23, 2026
Note that the the last record of the xport file for a dataset is padded with blanks to fill the 80 blocks used by xport files. When the data record length is less than the 80 bytes then it is possible for there to be room for extra records in that padding. In those cases SAS will ignore data records that are all blanks at the end of the last block of the file when reading the file.
One way to detect these is to decrement your calculated number of observations for every all blank data record detected at the end of the last block.
You can see that in action in this improved SAS macro for reading xport files.
(https://github.com/sasutils/macros/blob/master/xport2sas.sas)