Percent-Encode a String

Vim, 67 bytes/keystrokes

:s/\c[^a-z!'()*0-9._-]/\='%'.printf("%02x",char2nr(submatch(0)))/g<cr>

Note that <cr> represents the enter key, e.g. 0x0D which is a single byte.

This is a pretty straightforward solution. Explanation:

:s/                                                                    "Search and replace
   \c                                                                  "Case-insensitive
     [^a-z!'()*0-9._-]/                                                "A negative range. Matches any character not alphabetc, numeric or in "!'()*0-9._-"
                       \=                                              "Evaluate
                         '%'                                           "a percent sign string
                            .                                          "Concatenated with
                             printf("%02x",char2nr(submatch(0)))       "The hex value of the character we just matched
                                                                /g     "Make this apply to ever match
                                                                  <cr> "Actually run the command

That printf("%02x",char2nr(submatch(0))) garbage is terribly ungolfy.


Perl, 40 bytes

39 bytes code + -p.

A bit lame, but I think it's the shortest solution...

s/[^!'()*-.\w]/sprintf'%%%02x',ord$&/ge

Usage

echo -n ' !"#$%&'\''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqstuvwxyz{|}~' | perl -pe "s/[^'()*-.\w]/sprintf'%%%02x',ord$&/ge"
%20%21%22%23%24%25%26'()*+,-.%2f0123456789%3a%3b%3c%3d%3e%3f%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5b%5c%5d%5e_%60abcdefghijklmnopqstuvwxyz%7b%7c%7d%7e

Julia, 47 bytes

!s=replace(s,r"[^\w!'()*.-]",c->"%"hex(c[1],2))

Try it online!