26
\$\begingroup\$

Objective

Given an ASCII character, toggle its "shift-ness" on the standard US keyboard (ANSI-INCITS 154-1988 (R1999)), then output it.

Mapping

) ↔️ 0
! ↔️ 1
@ ↔️ 2
# ↔️ 3
$ ↔️ 4
% ↔️ 5
^ ↔️ 6
& ↔️ 7
* ↔️ 8
( ↔️ 9
" ↔️ '
+ ↔️ =
, ↔️ <
- ↔️ _
. ↔️ >
/ ↔️ ?
: ↔️ ;
(Capital Latin letters) ↔️ (Small Latin letters)
[ ↔️ {
\ ↔️ |
] ↔️ }
` ↔️ ~
(ASCII whitespaces and control characters are left intact)

Rule

  • Non-ASCII characters fall into don't care situation.

Examples

Given a character 0, output ).
Given a character C, output c.
Given a character (space), output .
Given a character \t (horizontal tab), output \t.
Given a character \a (bell), output \a.

\$\endgroup\$
  • \$\begingroup\$ Is it ok if we do the UK keyboard? Just four differences that I can tell. \$\endgroup\$ – ouflak Feb 26 at 18:08
  • \$\begingroup\$ @ouflak I'm afraid not. \$\endgroup\$ – Dannyu NDos Feb 26 at 20:01

14 Answers 14

8
\$\begingroup\$

x86-16, Genuine IBM PC, 32 31 29 bytes

00000000: b4f0 8ec0 bfe5 e8b3 3ab1 74a0 8200 f2ae  ........:.t.....
00000010: 3acb 7f02 f7db 268a 41ff cd29 c3         :.....&.A..).

Build with xxd -r.

Unassembled listing:

B4 F0       MOV  AH, 0F0H               ; BIOS segment address (F000H)
8E C0       MOV  ES, AX                 ; set ES to BIOS segment for SCASB
BF E8E5     MOV  DI, E8E5H              ; set to LABEL K10 
B3 3A       MOV  BL, 58                 ; size of table 
B1 74       MOV  CL, 58*2               ; size of both tables 
A0 0082     MOV  AL, BYTE PTR DS:[82H]  ; get input char from command line 
F2/ AE      REPNZ SCASB                 ; search BIOS table for char 
3A CB       CMP  CL, BL                 ; found in the lowercase table? 
7F 02       JG   UPPER                  ; if so, convert with uppercase table by adding
F7 DB       NEG  BX                     ; otherwise subtract the offset 
        UPPER: 
26:8A 41 FF MOV  AL, ES:[BX+DI-1]       ; get char from table
CD 29       INT  29H                    ; DOS fast write to console 
C3          RET                         ; return to DOS

Input/Output:

enter image description here

How does it work?

Well, the PC BIOS already contains all of the code and tables necessary for this since it actually handles the conversion of scan codes received from the keyboard to ASCII chars (in real/DOS mode at least). There's no way (that I know of) to actually hook this on just any "PC-compatible" BIOS, however if you know the location of the table in ROM (on the IBM PC it starts at F000:E8E5), you can use that.

On Page A-25 of Appendix A the IBM PC Technical Reference (listing of the entire source code of the PC BIOS) is the disassembly of this table:

enter image description here

Unfortunately, this address will be different on any given PC-clone BIOS and of course, there's no guarantee that it would even be implemented in the same way by anyone else. Thus, this submission is guaranteed to run only on a Genuine IBM PC. Go Big Blue!

|improve this answer|||||
\$\endgroup\$
8
\$\begingroup\$

JavaScript (Node.js),  110 ... 97  96 bytes

c=>(s=")0@2^6&7*8(9\"'+=-_:;`~")[s.indexOf(c)^1]||(B=Buffer)([([n]=B(c),n%127>32)<<4+n/64^n])+''

Try it online!

How?

We use a lookup table for these pairs:

) ↔️ 0
@ ↔️ 2
^ ↔️ 6
& ↔️ 7
* ↔️ 8
( ↔️ 9
" ↔️ '
+ ↔️ =
- ↔️ _
: ↔️ ;
` ↔️ ~

For all other characters, we use the following code:

(B = Buffer)([   // generate a buffer from a singleton array:
  (              //
    [n] = B(c),  //   n = ASCII code of the input character
    n % 127 > 32 //   1 if 32 < n < 127, or 0 otherwise
  )              //
  << 4 + n / 64  //   left-shifted by 4 if n < 64, or by 5 otherwise
  ^ n            //   XOR'ed with n
]) + ''          // end of Buffer(); coerce it to a string

which supports these 3 cases:

  • If \$32<n<64\$, we XOR the ASCII code with \$16\$, which is what we need for:

    ! ↔️ 1
    # ↔️ 3
    $ ↔️ 4
    % ↔️ 5
    , ↔️ <
    . ↔️ >
    / ↔️ ?
    
  • If \$64\le n<127\$, we XOR the ASCII code with \$32\$, which toggles the case of letters and also works for these pairs:

    [ ↔️ {
    \ ↔️ |
    ] ↔️ }
    
  • If \$n\le32\$ or \$n=127\$, the character is left unchanged (ASCII code XOR'ed with \$0\$).

|improve this answer|||||
\$\endgroup\$
7
\$\begingroup\$

Retina 0.8.2, 34 bytes

T`-~A-]d';-?/.+,:"(*&^%$#@!)}-_`Ro

Try it online! Link includes test cases.

Explanation: The T command is Retina's transliteration operation. Among its special characters are:

  • `: Delimit the parts of the command
  • -: Introduce a range (this suppresses the following special characters so d-o just means defghijklmno)
  • _: Delete the matching character
  • d: 0-9
  • R: Reverse the next range
  • o: Take the source string as the range

The trick is therefore to create as many ranges as possible to reduce the length, plus avoiding having the - and _ characters where they would be interpreted as a special character and avoiding the ` at all by including it inside a range. Additionally arranging for the range to transliterate to its reversal allows that to be expressed very cheaply.

|improve this answer|||||
\$\endgroup\$
4
\$\begingroup\$

Bash + core utilities, 58 \$\cdots\$ 51 49 bytes

Added 47 50 bytes to fix a bug kindly pointed out by Giuseppe.
Saved 8 bytes thanks to Value Ink!!!
Saved 2 bytes thanks to Mitchell Spector!!!

a='"A-]`=<_>?)!@#$%^&*(;'
b=\'a-~+-:
tr $a$b $b$a

Try it online!

|improve this answer|||||
\$\endgroup\$
  • 1
    \$\begingroup\$ I like the idea behind this, but it doesn't seem to be coming out right. For example, 'A' => 'b'. Also, it's not actually bash -- it's "bash + core utilities", so I think the language listed should be changed accordingly. (tr isn't a bash built-in.) \$\endgroup\$ – Mitchell Spector Feb 23 at 22:37
  • 1
    \$\begingroup\$ @MitchellSpector Sorry it's a bit of a hack, did this from my phone! T_T Will fix it when I get a chance. \$\endgroup\$ – Noodle9 Feb 23 at 23:01
  • 1
    \$\begingroup\$ From your phone? That's impressive in itself! I don't think I could do that. \$\endgroup\$ – Mitchell Spector Feb 23 at 23:03
  • 1
    \$\begingroup\$ @MitchellSpector All fixed now. \$\endgroup\$ – Noodle9 Feb 27 at 2:07
  • 1
    \$\begingroup\$ Nice solution! - \$\endgroup\$ – Mitchell Spector Feb 27 at 2:25
3
\$\begingroup\$

Python 3, 99 95 bytes

Same strategy as @Arnauld's answer.

-4 bytes thanks to @mypetlion

lambda c,s=")0!1@2#3$4%5^6&7*8(9\"'+=,<-_.>/?:;[{\\|]}`~":[c.swapcase(),s[s.find(c)^1]][c in s]

Try it online!

Explanation

  • s=")0!1@2#3$4%5^6&7*8(9\"'+=,<-_.>/?:;[{\\|]}`~" contains all pairs of special toggles.
  • s[s.index(c)^1] gets the index of the other character from the same pair
  • If the character is a Latin or control character (c in s is False), c.swapcase() is returned. c.swapcase() toggles the case of a Latin character, but doesn't affect control character.
|improve this answer|||||
\$\endgroup\$
  • 1
    \$\begingroup\$ Change everything after the colon to [c.swapcase(),s[s.find(c)^1]][c in s] to save 4 bytes. \$\endgroup\$ – mypetlion Feb 24 at 18:13
  • 1
    \$\begingroup\$ Clever use of boolean index! \$\endgroup\$ – Surculose Sputum Feb 24 at 18:26
  • 1
    \$\begingroup\$ Thanks @mypetlion for spotting the erroneous extra space at the end! \$\endgroup\$ – Surculose Sputum Feb 24 at 20:12
2
\$\begingroup\$

APL (Dyalog Extended), 86 bytesSBCS

This solution has been out-golfed but is preserved due to being interesting for its sourcing of the data.

Anonymous prefix lambda. Requires 0-based indexing (⎕IO←0).

{⍵∊a←↑'[!-~]{2}'⎕S'&'↓⍉↑'\w\w...'⎕R(5⍴'')↓11 71↑30↓⎕FMT⌂notes.keyboards:a⌷⍨~@1⊃⍸⍵=a⋄⍵}

Try it online!

This works by extracting the character pairs from a built-in note about keyboards.

{} "dfn"; argument character is :

⌂notes.keyboards the built-in string

⎕FMT ForMaT as a character matrix

30↓ drop the first 30 lines

11 71↑ take the first 11 lines' first 71 columns

 split into list of strings

'\w\w...'⎕R() PCRE Replace word-character,word-character,any-symbol,any-symbol with:

  '' the empty string

  5⍴ reshaped to length 5, padding with spaces; i.e. 5 spaces

 mix list of strings into character matrix

 transpose

 split matrix into list of strings

'[!-~]{2}'⎕R'&' PCRE Search and return any pair of printable ASCII characters

 mix list of character pairs into two-column matrix

a← store in a (for all)

⍵∊: if the argument character is a member of that matrix:

  ⍵= Boolean matrix mask where they are equal

   get indices where true

   pick the first (row,column) pair

  ~@1 negate the number at position 1 (the column; i.e. 1→0 and 0→1)

  a⌷⍨ use that to index into the matrix of all pairs

 else:

   the argument character

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Is taking the first 71 columns necessary? \$\endgroup\$ – Jonathan Allan Feb 22 at 23:53
  • \$\begingroup\$ Could '\w\w...'⎕R(5⍴'') be replaced by a reverse-each line (so that the Caps<>Lock mapping is never used)? \$\endgroup\$ – Jonathan Allan Feb 22 at 23:55
  • \$\begingroup\$ @JonathanAllan The 71 chops "Backspace" into "Backs" and reversing would expose "Enter" instead. \$\endgroup\$ – Adám Feb 23 at 0:07
  • \$\begingroup\$ Ah, I see, so you'd need to ignore all characters paired with spaces too. \$\endgroup\$ – Jonathan Allan Feb 23 at 0:11
2
\$\begingroup\$

Jelly,  38  37 bytes

“Æz⁶ɦG!€u<ʂCP]ƭƝ’œ?ØṖḟØB¤ḊØD;ṙ21,ƊyŒs

A monadic Link accepting a list of characters which yields a list of characters.

Try it online!

How?

Forms a mapping for non alphabetic characters, applies the translate atom, y, and then swaps case.

“Æz⁶ɦG!€u<ʂCP]ƭƝ’œ?ØṖḟØB¤ḊØD;ṙ21,ƊyŒs - Link: list of characters, S
“Æz⁶ɦG!€u<ʂCP]ƭƝ’                     - base 250 number              =53994992086540427749431907521542401
                        ¤             - nilad followed by link(s) as a nilad:
                   ØṖ                 -   printable characters       = !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
                      ØB              -   base-62 characters         =0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
                     ḟ                -   filter discard             = !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
                 œ?                   - lexicographical permutation  = '=<_>?;{|}~)!@#$%^&*("+,-./:[\]`
                         Ḋ            - dequeue                      ='=<_>?;{|}~)!@#$%^&*("+,-./:[\]`
                          ØD          - digit characters             =0123456789
                            ;         - concatenate                  =0123456789'=<_>?;{|}~)!@#$%^&*("+,-./:[\]`
                                 Ɗ    - last three links as a monad:
                              21      -   twenty-one
                             ṙ        -   rotate left                =)!@#$%^&*("+,-./:[\]`0123456789'=<_>?;{|}~
                                ,     -   pair                       =)!@#$%^&*("+,-./:[\]`0123456789'=<_>?;{|}~ and 0123456789'=<_>?;{|}~)!@#$%^&*("+,-./:[\]`
                                  y   - translate (S)
                                   Œs - swap case
|improve this answer|||||
\$\endgroup\$
2
\$\begingroup\$

05AB1E, 53 36 35 bytes

.šžQžiмDœ•1ĀêŽƵÆ{çIΣeÿí‚Ð"pkālrÙ•è‡

-1 byte implicitly thanks to @Grimmy due to his comment.

Try it online or verify all printable ASCII characters. NOTE: The TIO versions use the S•...•.I (builtin for the n'th permutation of a list) instead of œ•...•è (push all permutations of the string (extremely slow bottleneck for a string this large), and index into it).

Explanation:

.š        # Switch-case of the (implicit) input-character (if it's a letter)
  žQ      # Push all printable ASCII characters:
          #   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
    žiм   # Remove all letters [A-Za-z]:
          #   !"#$%&'()*+,-./0123456789:;<=>?@[\]^_`{|}~
  D       # Duplicate it
   œ      # Push a list of all permutations of this string
    •1ĀêŽƵÆ{çIΣeÿí‚Ð"pkālrÙ•
         "# Push compressed integer 540470878544692028277787799913384202055137480696656
     è    # Index it into the string-list of permutations:
          #   1'3457"908=<_>?)!@#$%^&*(;:,+./2{|}6-~[\]`
  ‡       # Transliterate the characters at the same indices
          # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why •1ĀêŽƵÆ{çIΣeÿí‚Ð"pkālrÙ• is 540470878544692028277787799913384202055137480696656.

This number(+1) is generated by the Jelly builtin Œ¿, which gets the 1-based index of the permutation.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Oooh we have an nth-permutation built-in, why did i never notice that! Though I guess it doesn't actually save any bytes over œ + è... \$\endgroup\$ – Grimmy Feb 24 at 13:09
  • 1
    \$\begingroup\$ @Grimmy Hmm, seeing your comment actually made me realize that œ...è is a byte shorter here, since .I doesn't work on strings (only on lists). So I can drop the S. \$\endgroup\$ – Kevin Cruijssen Feb 24 at 13:11
2
\$\begingroup\$

Perl 5 -p, 92 91 bytes

-1 byte thanks to @ValueInk

s/[]a-z{}|\\[]/$&^$"/gei;y/0-9)!@#$%^&*(`~\-=_+';":,.\/<>?/)!@#$%^&*(0-9~`_+\-=":';<>?,.\//

Try it online!

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ /[]a-z{}|\\[]/ lets you cut out the extra backslash while still matching ] in your first regex. Also, why did you put those particular characters into its own s/// operation instead of wrapping them into the y///? I don't know much about Perl but I did use the Ruby y/// analogue (tr) in my own solution without needing to do a regex sub at all. Maybe that could help you out with golfing your solution. \$\endgroup\$ – Value Ink Feb 25 at 5:03
  • \$\begingroup\$ You don't need the substitution, but it works out to be a couple of bytes shorter than accounting for those characters in the translation. \$\endgroup\$ – Xcali Feb 25 at 17:59
  • \$\begingroup\$ Hmmm. Could you have 2 strings (one with upper only and one with lower only) and pass them into tr in different orders similar to what I did in my Ruby solution? I'm not sure how costly it is to declare variables in Perl though \$\endgroup\$ – Value Ink Feb 26 at 0:27
  • \$\begingroup\$ No. tr/// is a quote-like operator in Perl. It does not do variable interpolation. You'd have to put it inside an eval, costing even more bytes. \$\endgroup\$ – Xcali Feb 26 at 1:32
  • \$\begingroup\$ I figured it out; eval might cost bytes, but not as much as it saves. 77 bytes, probably can be golfed further depending on how the strings are set up \$\endgroup\$ – Value Ink Feb 26 at 2:19
2
\$\begingroup\$

Pure Bash, 170 165 bytes

a=`echo {A..Z}`')!@#$%^&*("+,-./:[\]`'
b=`echo {a..z}`0123456789"'=<_>?;{|}~"
c=$a$b
d=$b$a
for((;n<164;n++)){ [ "$1" = "${c:n:1}" ]&&echo ${d:n:1}&&exit;}
echo "$1"

Try it online!

I decided to implement a solution inspired by @Noodle9's very nice answer, but using pure bash without any utilities (like tr).

Note that you need to run this in an empty directory; this can be fixed at the cost of 2 bytes by using quotes for "{d:n:1}". (This issue only arises with a non-empty current directory when the input is 8, so the output is supposed to be *.)

On the other hand, you can gain 2 bytes if you don't require output for the input \n: just eliminate the quotes in the final echo.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ You save 3 bytes using echo {a..z} {0..9} like this. \$\endgroup\$ – Noodle9 Feb 27 at 8:56
  • \$\begingroup\$ @Noodle9 Thanks for the suggestion. I had tried that before posting, but it was a little bit longer that way. (The problem is that {0..9} expands to 0 1 2 3 4 5 6 7 8 9, so I needed to add spaces into the other string to make the two strings still match up correctly.) But maybe you figured out some clever way of dealing with that? (Your link looks like it's just a link to your original answer, not to whatever you intended to link to.) \$\endgroup\$ – Mitchell Spector Feb 27 at 19:49
  • \$\begingroup\$ Sorry I sent the wrong link. I didn't perform any magic, simply substituted {0-9} for 0123456789 and it worked for your test. So incorrect then. :-( \$\endgroup\$ – Noodle9 Feb 27 at 23:01
  • \$\begingroup\$ @Noodle9 That's a shame -- I was hoping you had come up with some cool trick for getting past that issue! (If it worked for you on some test, you must have gotten lucky with the particular input you happened to try.) \$\endgroup\$ – Mitchell Spector Feb 27 at 23:10
  • \$\begingroup\$ @Noodle9 Unfortunately, that doesn't work :( . If you look at the test in your link, you'll see that, for instance, b gets mapped to b, instead of to B. I investigated it a bit when I was working on this answer, and it turns out that the {A..Z} construct isn't expanded on the right side of an assignment statement. \$\endgroup\$ – Mitchell Spector Feb 27 at 23:59
1
\$\begingroup\$

APL (Dyalog Extended), 60 bytesSBCS

Anonymous tacit prefix function.

-(,⊃⍨⍳-¯1*⍳)⍨('"''+=,<-_.>/?:;[{\|]}`~',∊')!@#$%^&*(',¨⎕D),-

Try it online!

- swap case

(), prepend the following:

⎕D the digits

')!@#$%^&*(',¨ prepend each of these characters to the corresponding digit

ϵnlist (flatten)

'"''+=,<-_.>/?:;[{\|]}`~', prepend these characters ('' is an escaped single quote)

-()⍨ apply the following function with that as left argument and the case-swapped original argument as right argument:

ɩndex of the case-swapped argument in the substitution string

¯1* negative one raised to that power

⍳- subtract that from the ɩndex of the case-swapped argument in the substitution string

,⊃⍨ use that to pick an element from the case-swapped argument appended to the substitution string

|improve this answer|||||
\$\endgroup\$
1
\$\begingroup\$

Pure Bash, 217 bytes

P=printf\ 
a=`$P%x "'$1"`
case $a in 3[ab])x=1;;2[27])x=5;;2[68]|3[79])x=17;;2a|38)x=18;;2b|3d)x=22;;29|30)x=25;;36|5e)x=104;;2d|32|40|5f)x=114;;20|7f)x=0;;60|7e)x=30;;[23]?)x=16;;[4-7]?)x=32
esac
$P\\`$P%o $[0x$a^x]`

Try it online!

Just bash -- no Unix utilities.

Input character is passed as an argument, and the output is on stdout.

_____________________________________________________________

Test runs:

$ cat shifttoggle
P=printf\ 
a=`$P%x "'$1"`
case $a in 3[ab])x=1;;2[27])x=5;;2[68]|3[79])x=17;;2a|38)x=18;;2b|3d)x=22;;29|30)x=25;;36|5e)x=104;;2d|32|40|5f)x=114;;20|7f)x=0;;60|7e)x=30;;[23]?)x=16;;[4-7]?)x=32
esac
$P\\`$P%o $[0x$a^x]`


$cat shifttoggleTest
#!/bin/bash

count=1

for s in ')!@#$%^&*("+,-./:' "0123456789'=<_>?;" ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz '[\]`' '{|}~' $' \a\n\r\t\x7F\x80\x88\x8F'
  do
    echo "TEST #$count"

    for ((n=0;n<${#s};n++))
      do
        c="${s:$n:1}"
        d=`./shifttoggle "$c"`

    # Backticks remove any trailing newline, so restore that here for the case where c is \n (you can check that shifttoggle really prints \n in that case).
    if test -z "$d"
          then
            d=$'\n'
        fi

    if test "$count" -eq 7
          then
            printf "  ASCII %d => ASCII %d\n" "'$c" "'$d"
      else
            echo "  $c => $d"
        fi

      done

      (( count++ ))
      echo ''
  done


$ ./shifttoggleTest 
TEST #1
  ) => 0
  ! => 1
  @ => 2
  # => 3
  $ => 4
  % => 5
  ^ => 6
  & => 7
  * => 8
  ( => 9
  " => '
  + => =
  , => <
  - => _
  . => >
  / => ?
  : => ;

TEST #2
  0 => )
  1 => !
  2 => @
  3 => #
  4 => $
  5 => %
  6 => ^
  7 => &
  8 => *
  9 => (
  ' => "
  = => +
  < => ,
  _ => -
  > => .
  ? => /
  ; => :

TEST #3
  A => a
  B => b
  C => c
  D => d
  E => e
  F => f
  G => g
  H => h
  I => i
  J => j
  K => k
  L => l
  M => m
  N => n
  O => o
  P => p
  Q => q
  R => r
  S => s
  T => t
  U => u
  V => v
  W => w
  X => x
  Y => y
  Z => z

TEST #4
  a => A
  b => B
  c => C
  d => D
  e => E
  f => F
  g => G
  h => H
  i => I
  j => J
  k => K
  l => L
  m => M
  n => N
  o => O
  p => P
  q => Q
  r => R
  s => S
  t => T
  u => U
  v => V
  w => W
  x => X
  y => Y
  z => Z

TEST #5
  [ => {
  \ => |
  ] => }
  ` => ~

TEST #6
  { => [
  | => \
  } => ]
  ~ => `

TEST #7
  ASCII 32 => ASCII 32
  ASCII 7 => ASCII 7
  ASCII 10 => ASCII 10
  ASCII 13 => ASCII 13
  ASCII 9 => ASCII 9
  ASCII 127 => ASCII 127
  ASCII 128 => ASCII 128
  ASCII 136 => ASCII 136
  ASCII 143 => ASCII 143
|improve this answer|||||
\$\endgroup\$
1
\$\begingroup\$

C (gcc), 100 bytes

char*strchrnul(),*s=")0@2^6&7*8(9\"'+=-_:;`~\0";f(c){c=s[strchrnul(s,c)-s^1]?:(c%127>32)<<4+c/64^c;}

Similar to the Javascript strategy. I'd like to get rid of the extra parentheses but this is pretty good.

Try it online!

|improve this answer|||||
\$\endgroup\$
1
\$\begingroup\$

Ruby -p, 66 bytes

Builds the "lowercase" and "uppercase" strings so that everything lines up, and connects them together to be passed into the tr function so it can do the proper conversions.

l="\\-a-z0-9=[-];',./`"
u='_A-Z)!@#$%^&*(+{|}:"<>?~'
$_.tr!u+l,l+u

Try it online!

|improve this answer|||||
\$\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.