15
\$\begingroup\$

My previous challenge, Print invisible text was quite popular, likely due to how trivial it is.

However those more observant of you may have noticed that you're not really printing invisible text, because it's impossible to read what was inputted given only the output.

So I figured how about a real invisible text challenge.

Given a string consisting of only printable ASCII characters (0x20-0x7E), convert each character to a distinct Unicode character (in UTF-8 encoding) that is not one of the 95 printable ASCII characters (any UTF-8 character outside of the 0x20-0x7E range)

Input

A string of printable ASCII characters, either as a string or character array/list

Output

The input string with each character replaced with a distinct non-printable character. Each given character must have a corresponding non-printable character that is not used as the substitute for any other character.

If you are unable to print non-printable characters, you can output the character values instead.

For example if your code replaces all lowercase a's with 0x01, you may not use 0x01 as the substitution for any other characters.

Your code must also be deterministic. This means that if, given the string Hello, all lowercase l's are replaced with 0x03, your code must also replace all lowercase l's with 0x03 given any other string.

Testcases

It's somewhat difficult to write testcases for this challenge, so I'll simply show the output as a list of hexcodes

input     -> output
"Hello"   -> [0x01, 0x02, 0x03, 0x03, 0x04]
"Hi!"     -> [0x01, 0x05, 0x06]
""        -> []
"   H   " -> [0x07, 0x07, 0x07, 0x01, 0x07, 0x07, 0x07]
"yo! "    -> [0x08, 0x04, 0x06, 0x07]
\$\endgroup\$
  • 9
    \$\begingroup\$ There is no such thing as a UTF-8 character: UTF-8 is a serialisation of Unicode, not a charset. And if "non-printable" makes sense in a Unicode context, it's certainly much narrower than "all but 95 of the hundreds of thousands of allocated codepoints". \$\endgroup\$ – Peter Taylor May 30 '17 at 9:49
  • 11
    \$\begingroup\$ @PeterTaylor Given we're talking in terms of character hexcodes here, I assumed it was clear that when I say UTF-8 character I mean a Unicode character in the UTF-8 encoding. ASCII is also an encoding standard, not a charset, yet people have no issue with the term "ASCII character". Anyway, I'll edit the wording to clarify. \$\endgroup\$ – Skidsdev May 30 '17 at 9:53
  • 3
    \$\begingroup\$ Any particular reason UTF-8 is required? \$\endgroup\$ – CalculatorFeline May 30 '17 at 19:30
  • \$\begingroup\$ Can the input be empty? \$\endgroup\$ – Dennis May 31 '17 at 17:18
  • 1
    \$\begingroup\$ +1 for "If you are unable to print non-printable characters" \$\endgroup\$ – Robert Fraser Jun 2 '17 at 6:17

16 Answers 16

13
\$\begingroup\$

Jelly, 3 bytes

O²Ọ

Try it online!

Squares each codepoint.

\$\endgroup\$
  • \$\begingroup\$ Very similar to the Japt solution I came up with, expect I cubed the codepoint instead of squaring it - you OK with me posting it? \$\endgroup\$ – Shaggy May 30 '17 at 9:53
  • 2
    \$\begingroup\$ @Shaggy Japt ≠ Jelly, so you can post it. \$\endgroup\$ – Erik the Outgolfer May 30 '17 at 9:54
  • \$\begingroup\$ Clever solution, didn't think of squaring. \$\endgroup\$ – Skidsdev May 30 '17 at 9:55
  • 1
    \$\begingroup\$ Grand, just wanted to be sure, lest you thought I was just ripping off your solution :) \$\endgroup\$ – Shaggy May 30 '17 at 10:00
  • 4
    \$\begingroup\$ @Shaggy there's no guideline against porting a solution to another language ; if there was it would be terrible since there is usually one optimal algorithm which gives optimal implementations in most languages, and nobody but the first poster wouldn't be able to prove they came with the algorithm on their own. Of course if you indeed port someone else's solution, it's only fairplay to mention their answer. \$\endgroup\$ – Aaron May 30 '17 at 15:47
13
\$\begingroup\$

Whitespace, 39 36 bytes


  
   	  
 
  
 	
	 				  
	
  
 


Try it online!

Explanation

nssn  ; label 'loop'
ssstssn ; push 4 to use as a multiplication operand
sns   ; dup 4 to use as a heap address
sns   ; dup 4 to use as a heap address
tnts  ; getchar and store at address 4
ttt   ; retrieve the value at address 4
tssn  ; multiply the character value by 4
tnss  ; putchar output the new character
nsnn  ; jmp 'loop'

Originally I wanted to multiply by -0 or -1 since they would be the shortest digits possible to declare in Whitespace. TIO does not differentiate between -0 and +0 so that's out. Unfortunately while the tutorial/spec is ambiguous about how to interpret a negative value as a char TIO (rightly) throws an error about the invalid argument so that also isn't an option.

The next shortest working constant is 4 so we end up performing the same basic approach as the Powershell/Pyth solutions.


Whitespace, 56 53 bytes - maps to tag characters


  
   			                 
 
  
 	
	 				   	
  
 


Try it online!

Explanation

Effectively the same approach as the previous version except this uses 0xE0000 as the constant and adds instead of multiplies. This maps the visible ASCII characters to the corresponding Unicode Tag Character (the range U+E0000-U+E007F). The intended use for this range was to indicate the language of the text in a plaintext file however that use is discouraged. This code will output valid labels if you prefix strings with a 0x01 character.

The Unicode Standard says that characters in this range have no visible rendering so I feel this meets the spirit of the challenge better than the previous approach.

\$\endgroup\$
  • 5
    \$\begingroup\$ Using an invisible program to print invisible text. I like. \$\endgroup\$ – Mark May 30 '17 at 20:58
7
\$\begingroup\$

Japt, 5 2 bytes

cp

Try it online


Explanation

     :Implicit input of string U
c    :Map over the character codes of the string.
p    :Square them.
     :Implicit output of result.
\$\endgroup\$
  • \$\begingroup\$ Hmm, after closer examination, it seems that 126 ** 3 == 2000376, which isn't in the range [0..1114111]. You can still square though :) That's because UTF-8 ends there, while UTF-16 continues. \$\endgroup\$ – Erik the Outgolfer May 30 '17 at 10:00
  • 1
    \$\begingroup\$ @EriktheOutgolfer Ehm. UTF-8 has exactly the same range as UTF-16 by definition. (In theory, UTF-8 could store higher codepoints, using 5 or 6 bytes per codepoint, but that is illegal.) \$\endgroup\$ – Mr Lister May 31 '17 at 6:49
5
\$\begingroup\$

Brain-Flak, 33 bytes

Includes +1 for -c

{((({}){}){}<>)<>}<>{({}<>)<>}<>

Try it online!

# For each character
{
  # Multiply by 4 and move to the other stack
  ((({}){}){}<>)

# End loop
<>}

# For each character on the other stack
<>{

  # Copy it back (reverse the stack)
  ({}<>)<>

# End loop
}<>
\$\endgroup\$
4
\$\begingroup\$

Braingolf v0.6, 17 bytes

VRl1-M[R.*>v]R&@

Squares each char value then prints.

-1 byte thanks to Erik the Outgolfer's squaring solution

Braingolf v0.7, 6 bytes [non-competing]

{.*}&@

Also squares each value then prints, but v0.7 has the "foreach" {} loop

\$\endgroup\$
4
\$\begingroup\$

Mathematica, 48 bytes

FromCharacterCode[4Mod[Hash/@Characters@#,978]]&

Explanation:

                             Characters@#      & - Convert string to array of characters
                       Hash/@                    - Hash them all using default hash
                   Mod[                  ,978]   - apply a modulus which uniquely transforms each potential character's hash into a number
                  4                              - times by 4 to move values out of 0x20-0x7E.
FromCharacterCode[                            ]  - Convert array of numbers back to string

Interestingly of the two modulus options less than 1000 which changed the 96 characters into 96 unique values with modulus 978 the lowest two values were 7 then 33. Luckily times by 4 converts this to 28 and 132 which both just fall outside the visible range. If I used the other modulus of 784 then I needed to multiply by 18 to move the numbers outside the range.

Test case.

Note: extra backslashes in there as escape characters for " and \. Also character 0x7E doesn't seem to want to paste correctly.

Input: "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"

Output: enter image description here

The use of Hash came about as ToCharacterCode is really long. However hashing it was nearly as expensive. The easy mathematica way to do this would be 49 bytes:

FromCharacterCode[4ToCharacterCode@Characters@#]&
\$\endgroup\$
2
\$\begingroup\$

CJam, 8 5 bytes

l95f+

Try it online!

Adds 95 to each codepoint.

\$\endgroup\$
  • \$\begingroup\$ Isn't there a way to multiply or square instead? \$\endgroup\$ – NieDzejkob May 31 '17 at 14:49
  • \$\begingroup\$ @NieDzejkob No, this is based on the fact that Character + Long = chr(ord(Character) + Long). Character * Long = [Character] * Long. Character # Long = error (# is exponentiation in CJam). \$\endgroup\$ – Erik the Outgolfer May 31 '17 at 14:54
2
\$\begingroup\$

Pyth, 6 bytes

smC*4C

Try it here.

Multiplies each codepoint by 4.

\$\endgroup\$
2
\$\begingroup\$

PowerShell, 32 31 Bytes

-1 Thanks to neil, 99+ to 4*

[char[]]"$args"|%{[char](4*$_)}

multiplies 9 by each character code and prints it back.

\$\endgroup\$
  • \$\begingroup\$ Out of interest, would multiplying by a small number (4-9) work? \$\endgroup\$ – Neil May 30 '17 at 10:23
  • \$\begingroup\$ the lowest printable times 4 is greater than the largest printable, that's -1 - thanks! \$\endgroup\$ – colsw May 30 '17 at 10:47
1
\$\begingroup\$

05AB1E, 4 bytes

ÇnçJ

Try it online!

Squares each codepoint.

\$\endgroup\$
1
\$\begingroup\$

CJam, 4 bytes

lWf^

XORs each code point with -1. CJam's characters are 16 bits wide, so this maps code point n to code point 65535 - n.

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Decimal, 37 bytes

91D31030030012255D412D590D543D301291D

Explanation:

91D             ; declare jump 1
    310         ; push user input to stack
    300         ; duplicate
    300         ; duplicate
    12255D      ; push EOF to stack
    412D        ; compare top two values, pop, push result
    5 90D 5     ; if result == true, quit
    43D         ; multiply top two values, pop, push result
    301         ; print
    2           ; pop
91D             ; goto jump 1

Try it online!

\$\endgroup\$
  • \$\begingroup\$ so jumping to zero (90D) terminates? \$\endgroup\$ – Skidsdev Jun 2 '17 at 7:49
  • \$\begingroup\$ @Mayube Exactly. \$\endgroup\$ – MD XF Jun 2 '17 at 14:20
1
\$\begingroup\$

Google Sheets, 68 bytes

=ArrayFormula(Join("",IfError(Char(Code(Mid(A1,Row(A:A),1))^2),"")))

I wanted to post this to show how awkward it is to do some basic functions in Sheets. Do you want to do an operation to every character in a cell and out the concatenated result? You're at 42 bytes before you even act on those characters.

=ArrayFormula(Join("",Mid(A1,Row(A:A),1)))

Otherwise, this is the same as other solutions: square the code point of each character.

\$\endgroup\$
0
\$\begingroup\$

Python 3, 40 38 bytes

print([chr(ord(x)*9)for x in input()])

Try It Online!

\$\endgroup\$
0
\$\begingroup\$

C, 42 bytes

c;f(){while(~(c=getchar()))putwchar(c*c);}

Assumes a UTF-8 locale. Input is squared.

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Clean, 25 bytes

import StdEnv

map((+)'~')

A partial function literal.

Try it online!

Realistically:

f s = {# c+'~' \\ c <-: s}

Unboxed array comprehension over an unboxed array of the same type ({#Char} -> {#Char}). Clean will be able to determine that the uniqueness is transferrable (!u:{#Char} -> u:{#Char}), and that the size is the same as the input size. This means that if you pass a *String, every character will be destructively updated with the corresponding one in the output, meaning no memory allocation or movement is done and the graph node is fully reused.

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.