[Question]
  [
 [Sandbox](https://codegolf.meta.stackexchange.com/a/18273/78850) 
Have y'all ever written an answer with unprintable ASCII characters in it and wished that there was an easy way to represent those characters in a printable way? Well, that's why the [Control Pictures](https://unicode-table.com/en/blocks/control-pictures/) Unicode block was invented.
However, manually substituting these characters into one's answer is time-consuming, so that's what today's challenge is about: swapping out the nasty invisible characters for nice, readable characters.
## Input
You will be given strings that contain a mix of ASCII-only characters (i.e. the UTF-8 code point of each character will be in the range: \$0 \lt char \le 127\$). 
## Output
For all unprintable characters, replace it with its corresponding character in the Control Pictures Unicode range.
In other words:
* Characters in the range \$0 \lt char \lt 9\$ are replaced with their corresponding character
* Horizontal tabs and newlines (9 and 10) *aren't* replaced
* Characters in the range \$11 \le char \lt 32\$ are replaced with their corresponding character
* Spaces (32) *aren't* replaced
* The delete character (127) is replaced with its corresponding character: `␡`
## Tests
*Characters are given as escapes for nice formatting here, but each character will be replaced with the unprintable character*
```
In -> Out
\x1f\x1c\x1f\x1e\x1f\x1e\x1f\x1f\x1e\x1f\x1e\x1f -> ␟␜␟␞␟␞␟␟␞␟␞␟
Hello\x07World! -> Hello␇World!
\x01\x02\x03\x04\x05\x06\x07\x08\x0c\x0b\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f -> ␁␂␃␄␅␆␇␈␌␋␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␡
\r -> ␍
```
## Rules
* All standard loopholes are forbidden
* Character substitutions *must* be made according to the Control Pictures Unicode block
* Input will be given with the literal unprintable characters
## Scoring
This is code-golf so the answer with the fewest amount of bytes wins.
## Test-Case Generator
I have provided a test case generator for y'all. It prints inputs in the way they will be passed to your program and outputs the expected result. 
[Try it here!](https://tio.run/##XVJbbtswEPyWTrHwlxgzhin1lQDuEXqBwDAEmbYJ2JRAqYCav/QNNI9N2iZ9F73aXsTdtWwj7YewImd2Z2ek6kWzKH22Xk/tDJbJwUodQ5XXdSznufWTIq9tMnF@9Kz0Vh3HkVtVZWgg5H5aruKoboLzcxhBrxdHxSIPk2JRusLWfHViNKQaMg0PNDzU8EjDYw1PNBgGDCOG7w0DhhHDkBHsiJuG/Egzc1JuT5mXMi9lXsq8lHkp8zLmZZtZ0pyNuw020sUiJK2CWRmgBefh/mrjmG3MwJcNsDP2FAnNCY1tzW3CYzt/AynON4mID5X4j3aW@yMQkS2xG53cl1GK2SzA63QtcWSXtZUZ@9QYjqNtcKu8khwJzwhfEr4ifE34hvAt4TvC94TnhB8ILwmvCJHwmvCG8CPhJ8LPhLeEd4RfCL8SfiP8TviD8CfhL8LfhH8IL/gLtawwdUWTnLpqs2ytYScu@0oQcpYs2n827V4GwVbLfGtUQ3sidazEQ7DN8@DFEfShd/i0x2Xne/1fwGYoUVZBot3/ZUqt/wI "Python 3 – Try It Online")
## Leaderboards
Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.
To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:
```
# Language Name, N bytes
```
where `N` is the size of your submission. If you improve your score, you *can* keep old scores in the headline, by striking them through. For instance:
```
# Ruby, <s>104</s> <s>101</s> 96 bytes
```
If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the *last* number in the header:
```
# Perl, 43 + 2 (-p flag) = 45 bytes
```
You can also make the language name a link which will then show up in the leaderboard snippet:
```
# [><>](http://esolangs.org/wiki/Fish), 121 bytes
```
```
var QUESTION_ID=196014;
var OVERRIDE_USER=78850;
var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;function answersUrl(d){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+d+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(d,e){return"https://api.stackexchange.com/2.2/answers/"+e.join(";")+"/comments?page="+d+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(d){answers.push.apply(answers,d.items),answers_hash=[],answer_ids=[],d.items.forEach(function(e){e.comments=[];var f=+e.share_link.match(/\d+/);answer_ids.push(f),answers_hash[f]=e}),d.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(d){d.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),d.has_more?getComments():more_answers?getAnswers():process()}})}getAnswers();var SCORE_REG=function(){var d=String.raw`h\d`,e=String.raw`\-?\d+\.?\d*`,f=String.raw`[^\n<>]*`,g=String.raw`<s>${f}</s>|<strike>${f}</strike>|<del>${f}</del>`,h=String.raw`[^\n\d<>]*`,j=String.raw`<[^\n<>]+>`;return new RegExp(String.raw`<${d}>`+String.raw`\s*([^\n,]*[^\s,]),.*?`+String.raw`(${e})`+String.raw`(?=`+String.raw`${h}`+String.raw`(?:(?:${g}|${j})${h})*`+String.raw`</${d}>`+String.raw`)`)}(),OVERRIDE_REG=/^Override\s*header:\s*/i;function getAuthorName(d){return d.owner.display_name}function process(){var d=[];answers.forEach(function(n){var o=n.body;n.comments.forEach(function(q){OVERRIDE_REG.test(q.body)&&(o="<h1>"+q.body.replace(OVERRIDE_REG,"")+"</h1>")});var p=o.match(SCORE_REG);p&&d.push({user:getAuthorName(n),size:+p[2],language:p[1],link:n.share_link})}),d.sort(function(n,o){var p=n.size,q=o.size;return p-q});var e={},f=1,g=null,h=1;d.forEach(function(n){n.size!=g&&(h=f),g=n.size,++f;var o=jQuery("#answer-template").html();o=o.replace("{{PLACE}}",h+".").replace("{{NAME}}",n.user).replace("{{LANGUAGE}}",n.language).replace("{{SIZE}}",n.size).replace("{{LINK}}",n.link),o=jQuery(o),jQuery("#answers").append(o);var p=n.language;p=jQuery("<i>"+n.language+"</i>").text().toLowerCase(),e[p]=e[p]||{lang:n.language,user:n.user,size:n.size,link:n.link,uniq:p}});var j=[];for(var k in e)e.hasOwnProperty(k)&&j.push(e[k]);j.sort(function(n,o){return n.uniq>o.uniq?1:n.uniq<o.uniq?-1:0});for(var l=0;l<j.length;++l){var m=jQuery("#language-template").html(),k=j[l];m=m.replace("{{LANGUAGE}}",k.lang).replace("{{NAME}}",k.user).replace("{{SIZE}}",k.size).replace("{{LINK}}",k.link),m=jQuery(m),jQuery("#languages").append(m)}}
```
```
body{text-align:left!important}#answer-list{padding:10px;float:left}#language-list{padding:10px;float:left}table thead{font-weight:700}table td{padding:5px}
```
```
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="https://cdn.sstatic.net/Sites/codegolf/primary.css?v=f52df912b654"> <div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td><a href="{{LINK}}">{{SIZE}}</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td><a href="{{LINK}}">{{SIZE}}</a></td></tr></tbody> </table> 
```
  
[Answer]
# [Zsh](https://www.zsh.org/), ~~83 79~~ 78 bytes
*-4 bytes by changing to a recursive solution, (-0 from bugfix,) -1 by abusing unquoted empty parameters expanding to 0 words*
```
<<<${1+"${(#)$((x=#1,x>126?x+9122:(x<9)^(x<11)^(x<32)?x+9216:x))}`$0 ${1:1}`"}
```
~~[Try it online!](https://tio.run/##HY3BCsIwEETv@YqF5rBLpbArVBoT/RNBosV4sGAuwZJvj0kuMwxvmPnFV1mRsKzbFzyg3jEaQ5wpjq6GgTRicoM/pAvLfE3jwiIGk13oVpW521GoIeHZJKKsrLU6FlKqz26PJ4QP7DxNLKd8hnv0IYwO@0HDuVWhxnc97zSXPw)
[Try it online!](https://tio.run/##ZY7BDoIwEETvfMUGemhDQ9yiNdSiH2IwEJSIRpoIhyrpt1fgiJfdZN7szH77u28oo15rTUZUMRlpxAilNo@Q2yMKebJxhkIoanXGLtNEXFYq2IwESmUZc2FJNjBHoCtD51kQNOYNtbneoO1gxCRBsXcHqPq6beOcLkUzdrN1uqQPpdhCz8hTLBwARDAYA6@q@8CU9uzBdDC0Zu1PBZdp4day3PJs9y9nkk@vFM7/AA "Zsh – Try It Online")~~
[Try it online!](https://tio.run/##ZY7BDoIwEETvfMUGemhDQ9yiNdSiH2IwEJSIRpoIhyrpt2PhiJfdZN7szH77@9RQRietNRkxDslII0YotXmE3B5RyJONMxRCUaszdvETcVmpYDMSKJVlzJVkAz5BoStDN7EgaMwbanO9QdvBiEmCYu8OUPV128Y5XXpm7Garv6QPpdhCz8hTLBwARDAYA6@q@4BPe/ZgOhhas/angsu0cGtZbnm2@5czyf0rhZt@ "Zsh – Try It Online")
In arithmetic contexts, `#1` is the code of the first character of `$1`. We use it 7 times, so `x=#1,` saves us 2 bytes. There may be a more compact way to do `(x<9)^(x<11)^(x<32)`.
The `${1+foo}` expands to `foo` *unless* `$1` is undefined, providing our recursive base case. The `"`$0 ${1:1}`"` is the actual recursive call. When an empty parameter is unquoted, it is removed from the word list, thus causing the final recursion be run with zero arguments.
Running strings longer than about 35 characters with the recursive solution causes TIO to complain: "fork failed: resource temporarily unavailable". However, with usual resource limits on a desktop, there shouldn't be a problem.
**Potential changes:**
* With the flag `-oCPRECEDENCES`, `<` binds more tightly than `^`, which would save 6 bytes by removing parentheses.
* With the flag `-oEXTENDEDGLOB`, we gain access to backreferences using `(#m)` and the `$MATCH` parameter. An approach like Arnauld's Javascript answer could be used, but it ends up being longer than the purely arithmetic approach.
* I also tried a transliterate solution, but the larger codepoints of the control pictures grew the bytecount too quickly.
[Answer]
# [JavaScript (Node.js)](https://nodejs.org), ~~67~~ 66 bytes
*Saved 1 byte thanks to @Grimmy*
```
s=>s.replace(/[^ -~ \n]/g,c=>(B=Buffer)([226,144,B(c)[0]%94+128]))
```
includes a literal `TAB`
[Try it online!](https://tio.run/##Dc69DoIwGIXhWa7iWwxf0wJCiD/BMuAlOCImTW0Rgy1p0cXorWOn551OzkO8hZdumObE2JtaNF88r33q1DQKqTBrr5D8VhfTZT2TvMaGNy@tlSPYFsWW5WXJGpSk3XTrQ0nzYt8Rsmjr0AOHOGZggnkVOAaLXShKCXwiAA@Uw3l2g@lT7ezzdBfuFE6gIVX0jaQ13o4qHW2PGn2Y/QM "JavaScript (Node.js) – Try It Online")
### How?
We match all characters that are:
```
   +-------> neither printable
   |  +----> nor a tabulation
   |  | +--> nor a linefeed
  / \ | |
[^ -~\t\n]
```
and replace them with the UTF-8 sequence \$[226, 144, (n\bmod94)+128]\$, where \$n\$ is the ASCII code of the original character.
This is equivalent to generating the code point \$(n\bmod94)+9216\$. But given the length of the corresponding JS method names, that would be ~~74~~ 73 bytes in plain ES6:
```
s=>s.replace(/[^ -~ \n]/g,c=>String.fromCharCode(c.charCodeAt()%94+9216))
```
[Try it online!](https://tio.run/##bY5Ba8JAFITPza94FUp2iW6IlBaxK0gu3hU8NC0sm01MWd@TfYsX0b@e7sGTCAPfMDDD/JmzYRuGU5whtW7s9Mh6xSq4kzfWifL7F2a3lwZ/yn5q9Wobw4C96gId64MJdeoIq@zdrqOQb4v3YjGvPqQcOwqCQUOeTwETq2XCV@L8M7mikHDJABgKDc92US6za2YJmbxTnnrRCZbyIZlsnPfUxD0F3742uDsMDElIEWIwyOnF0bVgIhjv1ST9@gc "JavaScript (Node.js) – Try It Online")
[Answer]
# [C (gcc)](https://gcc.gnu.org/), 119 109 108 89 75 bytes
Thanks to ceilingcat and Arnauld for the suggestions.
Like my original submission, the UTF-8 code sequence is largely hard-coded, but instead of using string literals an integer is used instead. This takes advantage of the fact that the LSB of an integer (on a little-endian processor) is the first byte stored in memory, and `0` MSB bytes map to `NUL` which terminates the string. For unprintable characters that map to the control pictures, three bytes are used to represent the value; one byte is used for everything else.
One trick which I use is to force the comparison of a contiguous range of values to zero so that I can use unsigned comparisons. This wraps around any values below the range to high values, allowing me to use a single compare (the `d-9U>1`, which is true for x<9 or x>10.)
```
d;f(char*s){for(;d=*s++;printf(&d))d=d>126|d<32&d-9U>1?d%94<<16|8425698:d;}
```
[Try it online!](https://tio.run/##ZZBNT8JAEIbv/RVrjaS7BdMtUFq3xasH4o14EA9lp4UmlZJuURLgr1veKmqihyfzkX1mJqsHK63bllTu6HVaC8MPeVU7ihJhXFdt62LT5E6POKeEptIPjhQP/R4NovlU3tNNNIpjGRzDkT8OovCO1Km9Lja63FHGYtNQUd2up5aFKew1LTbOW1UQZweLsW4dE@b5JekqxuzFXuZAX2L2J/7r2f0v7yEry2qx9yZPVV3S1XcbHQl8MAQjMAZB9xKEAKu8JcA4rxvrATgSjoQj4Ug4Eo6EI@HICKRgeTmVfs@a/Jy0qC/Z43w2Q3LqMyGaxCgLxef/MtEott01xrFtzlnuiMZ1ubJO7YfOy3Rl2sH7GQ "C (gcc) – Try It Online")
## Original version (89 bytes)
As C doesn't handle UTF-8 natively, I output the hard-coded prefix bytes and compute the trailing byte (which is the only byte that requires changing) if I need to print a control picture glyph. Other than that, it's a pretty standard string-processing function.
```
d;f(char*s){for(;d=*s++;printf(d>126|d<32&d-9U>1?"\xE2\x90%c":"%2$c",d%94+128,d));}
```
[Try it online!](https://tio.run/##ZZBNT8JAEIbv/RXrKqa7LaZTEKgL9WTigXgjHqyHskuhSaWkW5QE@evUt4qa6OHJfGSfmcnq7lLrpjEqc/UqraQV@6ysXGUm0nqe2lT5us5cE1M4eDfjXnhputEsplue7O7CZBcFHc1veCe80Nw3najvUTjyjRDq0Jzna11szYKNbW3y8moVOw6msZc0X7uvZW4E2zuMtWuZtE/Pk7ZiDJMpA/oUF3/ivx73v7z7RVGUyS4YPpZVYc6@2@gQwLFBD/TBNRi0L8EIYFUwBxgXtGMDAIfgEByCQ3AIDsEhOBSBFMxPp5rfs4Y/JyXVKXuYTadIDj6Tsp5Y5aD4/Gcma8U229q6nAvBMlfWnieUc2iOOivSpW26bx8 "C (gcc) – Try It Online")
[Answer]
# [Python 3](https://docs.python.org/3/), 69 bytes
```
lambda s:s.translate({i%128:i%34+9216for i in{*range(-1,32)}-{9,10}})
```
[Try it online!](https://tio.run/##PYyxDsIgFAB3vwKHBtC2KcWobeLuH7h0QVvsMwgNMGgI344kRm@@u@XtZ6N5kqchKfG8jgK53tXeCu2U8BMJULD22EPBd9uuZXtpLAIEOmyycp9IxUre0liFrmRNjDQtFrQnkmBcPwxocpstAfrL0LfKS5pZ/eXzpJQZXs3hYqwa15jS9AE "Python 3 – Try It Online")
[Answer]
# [Retina 0.8.2](https://github.com/m-ender/retina/wiki/The-Language/a950ad7d925ec9316e3e2fb2cf5d49fd15d23e3d), ~~27~~ 24 bytes
```
T`-`␁-␟␡`[^	\n]
```
[Try it online!](https://tio.run/##VY5ZasJwGMSJu1bctS4xiQfIFXz2AIIPLkSoD4WgIL5LWjdQdLTVLlYlV5uLpOlfBMvAD@b7GGaGvdFzv@s4dUPS1bFBWDpxIWyj2Qm3@m3HUWVVcSWg6VVNvH8Ez3f8ZyO1nmkOgo3B0Hyq/IWEJ@bXS0TyeH3@QDAUfYjFE8lUOpPNPeYLxZJcVtTxtcQiXohXYkJMiZkbJhbEilgSa2JDgNgSO@KNeCf2xIH4ID6JL@KbOIqlp9s0@xc "Retina 0.8.2 – Try It Online") `T`ransliterates the unprintables to the appropriate control pictures, but the tabs and newlines are excluded (for a 3-byte saving). Note: No CRs in the test cases because I don't know how to demonstrate them in TIO. Code would probably work with nulls with the obvious adjustments for the same byte count, but I don't know how to test that either.
[Answer]
# [Red](http://www.red-lang.org), ~~94~~ 92 bytes
```
func[s][n: charset[not{	
}#" "-#"~"]parse s[any[p: change n(to sp p/1 % 94 + 9216)| skip]]s]
```
[Try it online!](https://tio.run/##LctNCsIwGIThtTnFmCIoItIiSnsC125DFqVNtFi@hCQiUuvVY/92w8O8TtXxpmohmS4Q9Ysq4aWgAtWjdF4FQSZ0K9YnHPyQ8B@XdnR4UdJH2OlHdwXaBgNvYY8pNshP2CPP0vPuC/9srJReRqesKgMIaXYRsK4hoTFUnGNuSUo2chhoGdDorqptDXsb19brfnENPvFqZh7/ "Red – Try It Online")
A port or @Arnauld's 74-byte [JavaScript solution](https://codegolf.stackexchange.com/a/196021/75681), don't forget to upvote his answer!
[Answer]
# [Perl 5](https://www.perl.org/) (`-pC -Mutf8`), 27 bytes
same as Neil's retina solution
```
y;-
-;␁-␈␋-␟␡
```
[Try it online!](https://tio.run/##VY7LSsNQGISprbYk0tp6qdpL6lY4ZiUIATduunHtCzSCEExI2oWrEm8VlDpaq/VOXm0epKfHEwTlh/mZgWG@wA29XRnZLbG/s23bjjxzMqJgCqvvELEgbohb9b6JREqrbjXVaVGNls4/tH790X/WaLue5@eP/NDrbP2UtCcGaWJk5rK5@YV8YdEslpbKleWV1bXq@sZmrd5oWv10JCbOiQvikrgirlVZg90pNmJI3BMgHohHYkQ8EWPimXghJsQr8Ua8a9LPX7Rk6gfdE/80kiI4kOKw1z3emwE "Perl 5 – Try It Online")
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), 24 bytes
```
32R;Ø⁷’ḟ9ḟ⁵ż%94+⁽ ḥƊ$ỌFy
```
[Try it online!](https://tio.run/##AVQAq/9qZWxsef//MzJSO8OY4oG34oCZ4bifOeG4n@KBtcW8JTk0K@KBvSDhuKXGiiThu4xGef874oCcIC0@IOKAnTvDhwoxMjjhuLZzOOG7jMOH4oKsWf8 "Jelly – Try It Online")
A monadic link taking a Jelly string and returning a Jelly string with the desired substitutions.
[Answer]
# [Ruby](https://www.ruby-lang.org/) `-p`, 36 bytes
`\0` should really be a null byte but I was having trouble getting it in the code, oh well.
Input in STDIN should be the direct bytes but I put in some boilerplate code to make it more readable and obvious that they were the test cases.
```
$_.tr!"\0-
-","␀-␈␋-␟␡"
```
[Try it online!](https://tio.run/##ZY49CsJAEIVb0UO4GVK6Yce/aKG1N7BZCIkmEAgm5EdipaVgkxuIV8tBXF8gIGjx8Zhhv9mXV8HF2J7YiPDsJ8L2MDhlbpFWcjCS4ytNqG1usm3ubfNAPNvmRSarykLEkbAdsRVsSNccgUOf4U/@7WhIuzBJUl0rd5/mydHCBgODKZiBOViAZfcIrAA@UAHAEdUdUwAOw2E4DIfhMByGw3B4DXwQ9AWP3zJuV0Tn9E6zMk5PhZHZBw "Ruby – Try It Online")
]