Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

NAMESTR Records

The NAMESTR (Name String) record describes each variable in the dataset. Each record is exactly 140 bytes.

NAMESTR Layout

%%{init: {'theme': 'base', 'themeVariables': { 'fontSize': '11px'}}}%%
graph LR
    subgraph "NAMESTR Record (140 bytes)"
        A["0-1<br/>ntype"] --> B["2-3<br/>nhfun"]
        B --> C["4-5<br/>nlng"]
        C --> D["6-7<br/>nvar0"]
        D --> E["8-15<br/>nname"]
        E --> F["16-55<br/>nlabel"]
        F --> G["56-63<br/>nform"]
        G --> H["64-65<br/>nfl"]
        H --> I["66-67<br/>nfd"]
        I --> J["68-69<br/>nfj"]
        J --> K["70-71<br/>nfill"]
        K --> L["72-79<br/>niform"]
        L --> M["80-81<br/>nifl"]
        M --> N["82-83<br/>nifd"]
        N --> O["84-87<br/>npos"]
        O --> P["88-139<br/>rest"]
    end

Complete Field Reference

OffsetSizeFieldTypeDescription
0-12ntypei16Variable type: 1=numeric, 2=character
2-32nhfuni16Hash function (always 0)
4-52nlngi16Variable length in bytes
6-72nvar0i16Variable number (1-based)
8-158nnamechar[8]Variable name (space-padded)
16-5540nlabelchar[40]Variable label (space-padded)
56-638nformchar[8]Display format name
64-652nfli16Format length
66-672nfdi16Format decimal places
68-692nfji16Format justification (0=left, 1=right)
70-712nfilli16Unused padding
72-798niformchar[8]Input format name
80-812nifli16Informat length
82-832nifdi16Informat decimal places
84-874nposi32Position in observation
88-13952restchar[52]Reserved (zeros/spaces)

Field Details

ntype (Variable Type)

ValueMeaningStorage
1Numeric8 bytes, IBM float
2Character1-200 bytes, space-padded

nlng (Variable Length)

TypeValid RangeNotes
NumericAlways 8IBM float requires 8 bytes
Character1-200FDA maximum is 200 bytes

nname (Variable Name)

  • 8 bytes, right-padded with spaces
  • Uppercase letters A-Z, digits 0-9, underscore
  • Must start with a letter
  • Example: USUBJID (note trailing space)

nlabel (Variable Label)

  • 40 bytes, right-padded with spaces
  • Should be descriptive for data reviewers
  • Example: Unique Subject Identifier

Format Fields (nform, nfl, nfd, nfj)

The display format is stored across four fields:

#![allow(unused)]
fn main() {
// Example: DATE9. format
nform = "DATE    "   // Format name (8 bytes, space-padded)
nfl = 9              // Total width
nfd = 0              // Decimal places
nfj = 0              // Justification (0=left, 1=right)

// Example: 8.2 format (numeric with 2 decimals)
nform = "        "   // No named format
nfl = 8              // Total width
nfd = 2              // Decimal places
nfj = 1              // Right-justified (typical for numbers)

// Example: $CHAR200. format
nform = "$CHAR   "   // Format name with $ prefix
nfl = 200            // Total width
nfd = 0              // Not applicable for character
nfj = 0              // Left-justified (typical for text)
}

Informat Fields (niform, nifl, nifd)

Input format mirrors the display format structure but without justification:

FieldSizeDescription
niform8Input format name
nifl2Input format length
nifd2Input format decimals

npos (Position in Observation)

The byte offset of this variable within each observation row:

Observation Row:
[STUDYID      ][USUBJID      ][AGE     ][SEX]
^              ^              ^         ^
npos=0         npos=20        npos=60   npos=68

Parsing NAMESTR in Rust

#![allow(unused)]
fn main() {
use std::io::{Read, Cursor};
use byteorder::{BigEndian, ReadBytesExt};

struct Namestr {
    ntype: i16,
    nlng: i16,
    nvar0: i16,
    nname: String,
    nlabel: String,
    nform: String,
    nfl: i16,
    nfd: i16,
    nfj: i16,
    niform: String,
    nifl: i16,
    nifd: i16,
    npos: i32,
}

fn parse_namestr(bytes: &[u8; 140]) -> Namestr {
    let mut cursor = Cursor::new(bytes);
    
    let ntype = cursor.read_i16::<BigEndian>().unwrap();
    let _nhfun = cursor.read_i16::<BigEndian>().unwrap();
    let nlng = cursor.read_i16::<BigEndian>().unwrap();
    let nvar0 = cursor.read_i16::<BigEndian>().unwrap();
    
    let mut nname = [0u8; 8];
    cursor.read_exact(&mut nname).unwrap();
    let nname = String::from_utf8_lossy(&nname).trim_end().to_string();
    
    let mut nlabel = [0u8; 40];
    cursor.read_exact(&mut nlabel).unwrap();
    let nlabel = String::from_utf8_lossy(&nlabel).trim_end().to_string();
    
    // ... continue for remaining fields
    
    Namestr { ntype, nlng, nvar0, nname, nlabel, /* ... */ }
}
}

Writing NAMESTR in Rust

#![allow(unused)]
fn main() {
use std::io::Write;
use byteorder::{BigEndian, WriteBytesExt};

fn write_namestr<W: Write>(w: &mut W, var: &Variable, pos: i32) -> std::io::Result<()> {
    // ntype
    w.write_i16::<BigEndian>(if var.is_numeric { 1 } else { 2 })?;
    
    // nhfun (always 0)
    w.write_i16::<BigEndian>(0)?;
    
    // nlng
    w.write_i16::<BigEndian>(var.length as i16)?;
    
    // nvar0 (1-based variable number)
    w.write_i16::<BigEndian>(var.index as i16 + 1)?;
    
    // nname (8 bytes, space-padded)
    let mut name = [b' '; 8];
    name[..var.name.len().min(8)].copy_from_slice(var.name.as_bytes());
    w.write_all(&name)?;
    
    // nlabel (40 bytes, space-padded)
    let mut label = [b' '; 40];
    label[..var.label.len().min(40)].copy_from_slice(var.label.as_bytes());
    w.write_all(&label)?;
    
    // Format fields...
    // Informat fields...
    // npos
    w.write_i32::<BigEndian>(pos)?;
    
    // rest (52 bytes of zeros)
    w.write_all(&[0u8; 52])?;
    
    Ok(())
}
}

xportrs Format API

xportrs provides a high-level API for format handling:

#![allow(unused)]
fn main() {
use xportrs::{Column, ColumnData, Format};

// Create column with format metadata
let col = Column::new("AESTDTC", ColumnData::F64(vec![Some(23391.0)]))
    .with_label("Start Date/Time")
    .with_format(Format::parse("DATE9.").unwrap());

// The Format struct extracts:
// - name: "DATE"
// - length: 9
// - decimals: 0
// - justification: Right (default for formats)
}

Common Formats

FormatnformnflnfdDescription
DATE9.DATE 90Date (01JAN2024)
DATETIME20.DATETIME200Date and time
8.2 82Numeric with 2 decimals
BEST12.BEST 120Best representation
$CHAR200.$CHAR 2000Character (200 bytes)
$200.$ 2000Character shorthand

[!TIP] For FDA submissions, avoid custom formats. Use standard SAS formats like DATE9., DATETIME20., and simple numeric formats.