Codificar por cento uma sequência

13

Introdução

Como alguns de vocês devem saber, os URLs realmente têm uma lista de caracteres que fazem coisas especiais. Por exemplo, o /caractere separa as partes do URL, e ?, &e =caracteres são usados para passar parâmetros de consulta para o servidor. Na verdade, há um monte de personagens com funções especiais: $&+,/:;=?@. Quando você precisar usar esses caracteres na URL por qualquer outro motivo além das funções especiais, precisará fazer algo chamado codificação por cento .

A codificação em porcentagem é quando você pega o valor hexadecimal de um %caractere e o precede antes do início. Por exemplo, o caractere ?seria codificado como %3Fe o caractere &seria codificado como %26. Em um URL especificamente, isso permite que você envie esses caracteres como dados via URL sem causar problemas de análise. Seu desafio será pegar uma string e codificar em porcentagem todos os caracteres que precisam ser codificados.

O desafio

Você deve escrever um programa ou função que utilize uma única cadeia de caracteres composta por caracteres com pontos de código 00-FF (caracteres ASCII e ASCII estendido). Você precisará gerar ou retornar a mesma string com cada caractere codificado em porcentagem, se necessário. Built-ins que realizam essa tarefa não são permitidos, nem são brechas padrão. Para referência, aqui está uma lista de todos os caracteres que precisam ser codificados em porcentagem:

  • Caracteres de controle (Codepoints 00-1F e 7F)
  • Caracteres ASCII estendidos (Codepoints 80-FF)
  • Caracteres reservados ( $&+,/:;=?@ou seja, pontos de código 24, 26, 2B, 2C, 2F, 3A, 3B, 3D, 3F, 40)
  • Caracteres inseguros ( " <>#%{}|\^~[]`ou seja, pontos de código 20, 22, 3C, 3E, 23, 25, 7B, 7D, 7C, 5C, 5E, 7E, 5B, 5D, 60)

Aqui está a mesma lista, mas como uma lista de pontos de código decimais:

0-31, 32, 34, 35, 36, 37, 38, 43, 44, 47, 58, 59, 60, 62, 61, 63, 64, 91, 92, 93, 94, 96, 123, 124, 125, 126, 127, 128-255

Este é o código golf, pelo que o código mais curto em bytes (ou método de pontuação alternativo aprovado) vence!

Casos de teste

http://codegolf.stackexchange.com/  =>  http%3A%2F%2Fcodegolf.stackexchange.com%2F
[@=>]{#}  =>  %5B%40%3D%3E%5D%7B%23%7D
Test String  =>  Test%20String
ÑÉÐÔ®  =>  %D1%C9%D0%D4%AE
  =>  %0F%16%7F (Control characters 0F, 16, and 7F)
 ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ  =>  %80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF (Extended ASCII characters 80-FF)
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  =>  %20!%22%23%24%25%26'()*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E
GamrCorps
fonte
Você teria uma caixa de teste que mostra os caracteres de controle?
Leaky Nun
@LeakyNun done.
GamrCorps 14/08/16
Tenho certeza de que o codepoint EFnão contém o ponto de interrogação.
user48538
@ zyabin101 onde você achou isso? Eu não estou vendo isso.
GamrCorps
"Por exemplo, o caractere? Seria codificado como% EF ..."
user48538 14/08/16

Respostas:

2

Pitão, 30 28 26 bytes

L?hx+G+rG1CGbb+\%.HCbsmydz

experimente online

Explicação

L?hx+G+rG1CGbb+\%.HCbsmydz
L?hx+G+rG1CGbb+\%.HCb       First part, L defines the function y(b)
 ?hx+G+rG1CGbb+\%.HCb       ? is the ternary operator
  hx+G+rG1CGb               This part will be evaluated
  hx                        x will find the first occurence of a
                            character in a list. If it doesn't
                            find one, it will return -1. hx then
                            equals 0 (or false).
    +G+rG1CG                The list of allowed characters, a
                            concetanation (+) of the alphabet (G),
                            uppercase alphabet (rG1) and numbers
                            (CG, see below for details)
            b               The character to find in the list
             b              True branch of the ternary operator,
                            the character is allowed and returned.
              +\%.HCb       False branch, convert to hex and add %
                     smydz  The actual program
                      mydz  Map every character in the input (z)
                            using the function y on every d
                     s      Join the array, and implicit print.

CGé esse truque que gera um número enorme que contém todos os dígitos possíveis. Isso é perfeito, pois não nos importamos com duplicatas ao verificar se uma sequência está em outra.

Lars
fonte
Esta resposta não atende às especificações da pergunta. Existem mais caracteres permitidos do que apenas A-Za-z0-9. Por exemplo, .deve ser preservado em vez de traduzido para %2E. (cc: @GamrCorps)
DLosc
3

Vim, 67 bytes / pressionamentos de tecla

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

Observe que <cr>representa a tecla Enter, por exemplo, 0x0Dque é um único byte.

Esta é uma solução bastante direta. Explicação:

: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

Aquele printf("%02x",char2nr(submatch(0)))lixo é terrivelmente sem graça .

James
fonte
"Esse printf("%02x",char2nr(submatch(0)))lixo é terrivelmente ungolfy" e extremamente hacky
Leaky Nun
2

Perl, 40 bytes

Código de 39 bytes + -p.

Um pouco manco, mas acho que é a solução mais curta ...

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

Uso

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
Dom Hastings
fonte
2

Julia, 47 bytes

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

Experimente online!

Dennis
fonte
1

Python 3, 92 bytes

5 bytes graças ao orlp.

1 byte graças ao Sp3000.

import re;lambda s:''.join(re.match("[!'-*.0-9\w-]",c,256)and c or'%%%02X'%ord(c)for c in s)

Ideone it!

Freira Furada
fonte
re.match("[!'()*.0-9A-Za-z_-]",c)and c or'%%%02X'%ord(c)
orlp
@ SP3000 \winclui ASCII estendido
Leaky Nun
Também, '()*->'-*
Sp3000
Eu acho que \wfunciona com a opção 256( re.ASCII): ideone . Definitivamente funciona no Python 3 no ideone, e deve funcionar com u"..."strings no Python 2, mas o ideone parece fazer coisas divertidas com o último (por exemplo, print len(u"ÑÉÐÔ®")dá 10 no ideone, mas 5 no repl.it e no meu computador, apesar de todos serem 2,7. 10)
Sp3000 14/08/16
1

C, 83 bytes

f(char*p){for(;*p;++p)printf(isalnum(*p)||strchr("!'()*-._",*p)?"%c":"%%%02X",*p);}
orlp
fonte
1

Python, 86 bytes

lambda s:"".join(["%%%02X"%ord(c),c][c<"{"and c.isalnum()or c in"!'()*-._"]for c in s)

Porta da minha resposta C.

orlp
fonte
1

Ruby, 37 + 3 = 40 bytes

Execute com -p(3 bytes extras), como $ ruby -p percent_encode.rb:

gsub(/[^\w!'()*-.]/){"%%%02X"%$&.ord}
daniero
fonte
1

Geléia , 28 27 bytes

ḟØWḟ©“!'()*-.”Od⁴‘ịØH”%p®,y

Este é um link monádico. Experimente online!

Como funciona

ḟØWḟ©“!'()*-.”Od⁴‘ịØH”%p®,y  Monadic link. Argument: s (string)

 ØW                          Yield “0...9A...Z_a...z”.
ḟ                            Remove these characters from s.
     “!'()*-.”               Yield “!'()*-.”.
   ḟ                         Remove these characters from s.
    ©                        Copy the result to the register.
              O              Ordinal; get the code point of each character.
               d⁴            Divmod 16; yield quotient and remainder modulo 16.
                 ’           Decrement the results.
                  ịØH        Index into “0123456789ABCDEF”.
                     ”p%     Perform Cartesian product with ”%, prepending it to
                             each pair of hexadecimal digits.
                        ®,   Yield [t, r], where t is the string in the register
                             and r the result of the Cartesian product.
                          y  Use this pair to perform transliteration on s.
Dennis
fonte
1

Haskell, 201 179 178 127 119 bytes

import Data.Char;import Numeric;f=(=<<)(\c->if isAlphaNum c&&isAscii c||elem c"-_.~"then[c]else '%':(showHex$ord c)"")

Ungolfed:

import Data.Char
import Numeric

f=(=<<) e
e c = if isAlphaNum c && isAscii c && c `elem` "-_.~" then [c] else '%' : (showHex $ ord c) ""
sham1
fonte
Você pode remover um monte de espaços?
Rɪᴋᴇʀ
Você pode perder where, transformar os ifguardas, tornar parcial, perder o último argumento de showHex, em linha p, em linha s, perder a assinatura, reordenar eleme perder ainda mais espaço em branco. Como primeira aproximação, desci para 118 dessa maneira.
MarLinn
Agradeço à @MarLinn por várias boas sugestões sobre como reduzir o código. No entanto, tive alguns problemas com certas sugestões. Antes de tudo, se eu remover a assinatura, o GHC irá reclamar disso No instance for (Foldable t0) arising from a use of ‘foldr’. Ele diz que o tipo da função é ambíguo, resultando em uma ligação inferida de f :: t0 Char -> [Char]. E, segundo, não pude remover o argumento da string vazia do showHex, pois ele retorna um ShowS, que é um alias de tipo para a String -> Stringnecessidade da string vazia.
sham1
@ sham1, sim, ShowSpega uma String ... mas você tem uma: a que você está adicionando (++). Então você pode perder os dois ao mesmo tempo. É por isso ShowSque parece assim. Não recebo o erro de tipo, então acho que é uma coisa da versão? Duas outras coisas que notei até agora: otherwisesempre podem ser substituídas por 1<2(uma abreviação de True), mas se você retornar, ifpoderá incorporar ee soltar todos os nomes. E até transforme a dobra em a concatMap, ie a (>>=). Não economiza muito, mas pelo menos um pouco. Também pode resolver o erro de tipo.
MarLinn
0

Python 2, 78 bytes

lambda s:"".join(["%%%02x"%ord(c),c][c.isalnum()or c in"!'()*-._"]for c in s)

Mais bem formatado:

lambda s:
    "".join(["%%%02x" % ord(c), c][c.isalnum() or c in"!'()*-._"] for c in s)
Byte Commander
fonte
0

SQF , 199 176

Usando o formato de função como um arquivo:

i="";a="0123456789ABCDEF!'()*-.GHIJKLMNOPQRSTUVWXYZ_";{i=i+if((toUpper _x)in a)then{_x}else{x=(toArray[_x])select 0;"%"+(a select floor(x/16))+(a select(x%16))}}forEach _this;i

Ligar como "STRING" call NAME_OF_COMPILED_FUNCTION

Furioso
fonte
0

PowerShell v2 +, 146 bytes

param($n)37,38+0..36+43,44,47+58..64+91,93+96+123..255-ne33|%{$n=$n-replace"[$([char]$_)]",("%{0:x2}"-f$_)};$n-replace'\\','%5c'-replace'\^','%5e'

Muito tempo porque eu queria mostrar uma abordagem diferente, em vez de apenas copiar e colar a mesma cadeia de caracteres de regex que todo mundo está usando.

Em vez disso, passamos por todos os pontos de código que devem ser codificados em porcentagem e fazemos um literal -replacena string de entrada a $ncada iteração (salvando novamente em $n). Precisamos considerar os dois caracteres especiais que precisam ser escapados \e ^, portanto, esses -replaceelementos estão separados no final. Como não salvamos novamente a sequência final, ela é deixada no pipeline e a impressão está implícita.

AdmBorkBork
fonte
0

Conjunto x86 de 16/32 bits, 73 bytes

Código de bytes:

AC 3C 21 72 2A 74 3E 3C 26 76 24 3C 2B 72 36 3C
2C 76 1C 3C 2F 72 2E 74 16 3C 3A 72 28 74 10 3C
5F 74 22 50 0C 60 3C 60 74 02 3C 7B 58 72 16 D4
10 3C 09 1C 69 2F 86 E0 3C 09 1C 69 2F 92 B0 25
AA 92 AA 86 E0 AA E2 B8 C3

Desmontagem:

l0: lodsb         ;fetch a character
    cmp  al, 21h
    jb   l1       ;encode 0x00-0x20
    je   l2       ;store 0x21
    cmp  al, 26h
    jbe  l1       ;encode 0x22-0x26
    cmp  al, 2bh
    jb   l2       ;store 0x27-0x2A
    cmp  al, 2ch
    jbe  l1       ;encode 0x2B-0x2C
    cmp  al, 2fh
    jb   l2       ;store 0x2D-0x2E
    je   l1       ;encode 0x2F
    cmp  al, 3ah
    jb   l2       ;store 0x30-0x39
    je   l1       ;encode 0x3A
    cmp  al, 5fh
    je   l2       ;store 0x5F
    push eax
    or   al, 60h  ;merge ranges
    cmp  al, 60h
    je   l3       ;encode 0x40, 0x60
    cmp  al, 7bh
l3: pop  eax
    jb   l2       ;store 0x41-0x5A, 0x61-0x7A
                  ;encode 0x3B-0x3F, 0x5B-0x5E, 0x7B-0xFF

l1: aam  10h      ;split byte to nibbles
    cmp  al, 9    ;convert 0x0A-0x0F 
    sbb  al, 69h  ;to
    das           ;0x41-0x46 ('A'-'F')
    xchg ah, al   ;swap nibbles
    cmp  al, 9    ;do
    sbb  al, 69h  ;other
    das           ;half
    xchg edx, eax ;save in edx
    mov  al, '%'
    stosb         ;emit '%'
    xchg edx, eax
    stosb         ;emit high nibble
    xchg ah, al

l2: stosb         ;emit low nibble or original character
    loop l0       ;until end of string
    ret

Ligue com:
- esi = ponteiro para o buffer que contém a string de origem;
- edi = ponteiro para o buffer que recebe a string codificada;
- ecx = comprimento da cadeia de origem.

Peter Ferrie
fonte