Display words using 7-segment display My first ever Programming Puzzles &amp; Code Golf is the Seven-Slash Display. Here is my first Challenge, also based on 7-segment display.
I often think about, besides numbers, what kind of letters I can display using a simple 7 segment display. Turns out a lot of letters can be displayed. In fact, all but the letters K, M, V, W, X can be displayed using a single 7-segment display. This is because you can display either lower case or the upper case of a letter. e.g.
&quot;abcdef&quot; can be displayed as
 _     _     _  _
!_!!_ !   _!!_ !_ 
! !!_!!_ !_!!_ !  

Note that each character is a 3x3 matrix made up of ! and _.
Of course, the 7-segment display can be used to display numbers and symbols:
 _     _  _  _           _  _     _    
  ! _ !_ !_ !      _!  !!_ !_!!  !_!!_!
  !    _!!_ !_!   !_!  ! _!!  !_ ! ! _!

Some letters can have both upper and lower case:
 _                 _          
!   _ !_!!_   !   ! ! _ ! !   
!_ !_ ! !! !  !  !!_!!_!!_!!_!

Here are the full set of characters:
 _     _  _     _  _  _  _  _                       _
! !  ! _! _!!_!!_ !_   !!_!!_!    _ !_     _     _  _!
!_!  !!_  _!  ! _!!_!  !!_! _!   !_ ! !  !!_!!_!   !  

 _     _     _  _  _                 _  _  _     _           _ 
!_!!_ !   _!!_ !_ !  !_!  !  !!   _ ! !!_!!_! _ !_ !_ ! !!_! _!
! !!_!!_ !_!!_ !  !_!! !  !!_!!_ ! !!_!!    !!   _!!_ !_! _!!_ 

Pay attention that there are a white space( ), a dash(-) and a question mark(?). The letter I, O and Z are the same as numbers 1, 0 and 2 respectively.
In this challenge, you will write a program or a function to display a string using 7-segment display format above.
Rules

You can write a program or a function

This is code-golf, the shortest code in bytes wins

Your program or function should take input from STDIN or as a parameter. And outputs a string to STDOUT or as a string in 3 lines without leading space but terminated with newline. Handle upper/lower case of CHIOU properly.

You may optionally print trailing white spaces

You must follow the format above. Using underscore _ and exclamation mark ! to form your 7-segment display.

You must support white space( ), dash(-) and question mark (?)

If the string contain unsupported character (k, m, v, w, x), a single error character (3 horizon line, see example) is displayed. Besides the 5 unsupported characters, you can assume the input is made up of only supported character set.

I opted to not have a letter for lowercase L (l) due to confusions but if you are so inclined, you can display it like a 1 either to the right or left.


Examples
$./a.out Start
 _     _    
!_ !_ !_! _ !_
 _!!_ ! !!  !_
$./a.out &quot;7-seg dIsplay&quot;
 _     _  _  _           _  _     _ 
  ! _ !_ !_ !      _!  !!_ !_!!  !_!!_!
  !    _!!_ !_!   !_!  ! _!!  !_ ! ! _!
$./a.out &quot;0123456789 chiou-?&quot;
 _     _  _     _  _  _  _  _                       _
! !  ! _! _!!_!!_ !_   !!_!!_!    _ !_     _     _  _!
!_!  !!_  _!  ! _!!_!  !!_! _!   !_ ! !  !!_!!_!   !
$./a.out &quot;ABCDEFGHIJLNOPQRSTUZ&quot;
 _     _     _  _  _                 _  _  _     _        _
!_!!_ !   _!!_ !_ !  !_!  !  !!   _ ! !!_!!_! _ !_ !_ ! ! _!
! !!_!!_ !_!!_ !  !_!! !  !!_!!_ ! !!_!!    !!   _!!_ !_!!_
$./a.out &quot;abcdefghijlnopqrstuz&quot;
 _           _  _  _                    _  _     _        _
!_!!_  _  _!!_ !_ !  !_      !!   _  _ !_!!_! _ !_ !_     _!
! !!_!!_ !_!!_ !  !_!! !  !!_!!_ ! !!_!!    !!   _!!_ !_!!_
$./a.out &quot;Bad Form&quot;
 _
 _
 _
$./a.out &quot;Hello&quot;
    _       
!_!!_ !  !   _
! !!_ !_ !_ !_!
$./a.out &quot;World&quot;
 _
 _
 _
CJam, 123 114 112 110 bytes

Qq_eu";=KMVWX":T&amp;"@"@?" -chiou"Tereu{i"^A^W^G;^Þ¯     ^Þ^Û9³·^É¿»
^@
^P
^Ü^Ò½7¦^_¶´§=   ^O^V&amp;5^U¯¼¹^T³6/"=i2b9Ue[3/"!_!"f.{S?}.+}/N*


The above uses caret notation, since the code contains unprintable characters. One of them is a null byte (^@), which means that this code can only be executed from the command line.

At the cost of only two more bytes (for a total of 112), we can fix this.

Qq_eu";=KMVWX":T&amp;"@"@?" -chiou"Tereu{i"AWG{ÞïIÞÛyó÷Éÿû
@
P
ÜÒýwæ_öôç}IOVfuUïüùTóvo"=i448+2b1&gt;3/"!_!"f.{S?}.+}/N*


This time, all characters are printable. Try it online in the CJam interpreter.

Example run

$ LANG=en_US
$ xxd -ps -r &gt; 7seg.cjam &lt;&lt;&lt; 51715f6575223b3d4b4d565758223a5426224022403f22202d6368696f752254657265757b69220117073b9eaf099e9b39b3b789bfbb0a000a100a9c92bd37a61fb6b4a73d090f16263515afbcb914b3362f223d6932623955655b332f22215f2122662e7b533f7d2e2b7d2f4e2a
$ wc -c 7seg.cjam 
110 7seg.cjam
$ echo -n '0123456789 chiou-?' | cjam 7seg.cjam; echo
 _     _  _     _  _  _  _  _                       _ 
! !  ! _! _!!_!!_ !_   !!_!!_!    _ !_     _     _  _!
!_!  !!_  _!  ! _!!_!  !!_! _!   !_ ! !  !!_!!_!   !  
$ echo -n 'ABCDEFGHIJLNOPQRSTUYZ' | cjam 7seg.cjam; echo
 _     _     _  _  _                 _  _  _     _           _ 
!_!!_ !   _!!_ !_ !  !_!  !  !!   _ ! !!_!!_! _ !_ !_ ! !!_! _!
! !!_!!_ !_!!_ !  !_!! !  !!_!!_ ! !!_!!    !!   _!!_ !_! _!!_ 
$ echo -n 'World' | cjam 7seg.cjam; echo
 _ 
 _ 
 _


Idea (printable version)

Each character can be shown on the 9-segment display

!_!
!_!
!_!


by replacing some of its characters characters with spaces.

We can turn a specific character into an integer by replacing each shown segment, in natural reading order, with a 1, each not shown segment with a 0 and considering the result binary digits.

The first and third segment are never shown, so this will produce integers in the ranges [0,64) and [128,192).

We can encode each of these integers as a single byte, but half of them will result in unprintable characters. Thus, we add 64 to each integer before casting to character, which makes sure the code points are in the ranges [64,128) and [192,256).

The only unprintable character in these two ranges is DEL (code point 127), which corresponds to the following, unsued display configuration:

!_!
!_!


We can reverse the above encoding by adding 448 == 512 - 64 to each code point, converting to base 2 and removing the first binary digit.

All that's left to to find an efficient way of associating these encoded segmenets with their corresponding ASCII characters.

If we map the characters of " -chiou" to the characters of ";=KMVWX" and convert the entire input to uppercase, we can simply store encoding for all characters between 0 (code point 48) and Z (code point 90), giving a range of 43.

Array indexing is modular in CJam, so if A is a string of length 43, A86=, A43= and A0= all yield the same results. The character with code point 86 is V, so we simply store the encoded segments of V - Z and 0 - U, in order.

In the actual code, we select the at sign as "bad form" character, replace the entire input with the string "@" if it contains a forbidden letter and reverse the steps from above.

Code (printable version)

Q            e# Push an empty array for posterior concatenation.
q            e# Read from STDIN.
_eu          e# Copy the input and convert is to uppercase.
";=KMVWX":T&amp; e# Intersect the result with with T := ";=KMVWX".
"@"@?        e# Select "@" if truthy and the input if falsy.
" -chiou"Ter e# Perform transliteration.
eu           e# Convert everything to uppercase.
{            e# For each character in the modified input:
  i          e#   Push its code point.
  "…"=       e#   Select the corresponding character from the string.
  i448+      e#   Push that character's code point and add 448.
  2b1&gt;       e#   Convert to base 2 and discard the first digit.
  3/         e#   Group into triplets of digits (rows).
  "!_!"      e#   Push that string.
  f.{        e#   For each group of digits:
    S?       e#     Select the corresponding char of "!_!" for 1 and " " for 0.
  }          e#
  .+         e#   Concatenate along the rows with the previous results.
}/           e#
N*           e# Join, separating by linefeeds.