Given the RFC 1035 message diagram from Section 4.1.1 (Header section format) as a string:
http://www.ietf.org/rfc/rfc1035.txt

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      ID                       |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QDCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ANCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    NSCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ARCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Where (every column of the table is 1 bit):

ID is 16 bits
QR = Query (0) or Response (1)
Opcode = Four bits defining kind of query:
  0:    a standard query (QUERY)
  1:    an inverse query (IQUERY)
  2:    a server status request (STATUS)
  3-15: reserved for future use
AA = Authoritative Answer bit
TC = Truncation bit
RD = Recursion Desired bit
RA = Recursion Available bit
Z = Reserved
RCODE = Response code
QC = Question Count
ANC = Answer Count
AUC = Authority Count
ADC = Additional Count

Write a function, member function, class or template that accepts a similar multi-line string as input to define a data structure or something else able to decode or store a header with that specified bit structure.

If your language has macros, introspection, code generation, or powerful enough templates, then accept such string at compile-time to define the header data structure statically.

Such "Header" function or template should accept a table with 8, 16, 32 or 64 columns, and any number of rows. For simplicity the only allowed symbols to define the table are + - | (plus, minus, pipe), and whitespace. Lines of the input string composed just of whitespace should be ignored. Leading and trailing whitespace in the input string should be ignored, as well as before and after each table row. The box for each bit of the diagram takes four chars "+--+". The code should perform a little of validation of the input string, but for brevity a full validation is not required.

Bonus: perform a thoroughly validation of the input string.

#lang racket/base
(require (only-in racket/list drop-right)
         (only-in racket/string string-trim))
 
(provide ascii-art->struct)
 
;; reads ascii art from a string or input-port
;; returns:
;;   list of (word-number highest-bit lowest-bit name-symbol)
;;   bits per word
(define (ascii-art->struct art)
  (define art-inport
    (cond
      [(string? art) (open-input-string art)]
      [(input-port? art) art]
      [else (raise-argument-error 'ascii-art->struct
                                  "(or/c string? input-port?)"
                                  art)]))
  (define lines
    (for/list ((l (in-port (lambda (p)
                             (define pk (peek-char p))
                             (case pk ((#\+ #\|) (read-line p))
                               (else eof)))
                           art-inport)))
      l))
  (when (null? lines)
    (error 'ascii-art->struct "no lines"))
  (define bit-re #px"[|+]([^|+]*)")
  (define cell-re #px"[|]([^|]*)")
 
  (define bit-boundaries (regexp-match-positions* bit-re (car lines)))
 
  (define bits/word (sub1 (length bit-boundaries)))
 
  (unless (zero? (modulo bits/word 8))
    (error 'ascii-art->struct "diagram is not a multiple of 8 bits wide"))
 
  (define-values (pos->bit-start# pos->bit-end#)
    (for/fold ((s# (hash)) (e# (hash)))
              ((box (in-range bits/word))
               (boundary (in-list bit-boundaries)))
      (define bit (- bits/word box 1))
      (values (hash-set s# (car boundary) bit)
              (hash-set e# (cdr boundary) bit))))
 
  (define fields
    (apply append
           (for/list ((line-number (in-naturals))
                      (line (in-list lines))
                      #:when (odd? line-number))
             (define word (quotient line-number 2))
             (define cell-positions (regexp-match-positions* cell-re line))
             (define cell-contents (regexp-match* cell-re line))
             (for/list ((cp (in-list (drop-right cell-positions 1)))
                        (cnt (in-list cell-contents)))
               (define cell-start-bit (hash-ref pos->bit-start# (car cp)))
               (define cell-end-bit (hash-ref pos->bit-end# (cdr cp)))
               (list word cell-start-bit cell-end-bit (string->symbol (string-trim (substring cnt 1))))))))
  (values fields bits/word))