This task   (the reverse of   URL encoding   and distinct from   URL parser)   is to provide a function or mechanism to convert an URL-encoded string into its original unencoded form.

Task
URL decoding
You are encouraged to solve this task according to the task description, using any language you may know.


Test cases
  •   The encoded string   "http%3A%2F%2Ffoo%20bar%2F"   should be reverted to the unencoded form   "http://foo bar/".
  •   The encoded string   "google.com/search?q=%60Abdu%27l-Bah%C3%A1"   should revert to the unencoded form   "google.com/search?q=`Abdu'l-Bahá".



ABAP

<lang ABAP>REPORT Z_DECODE_URL.

DATA: lv_encoded_url TYPE string VALUE 'http%3A%2F%2Ffoo%20bar%2F',

     lv_decoded_url TYPE string.

CALL METHOD CL_HTTP_UTILITY=>UNESCAPE_URL

 EXPORTING
   ESCAPED   = lv_encoded_url
 RECEIVING
   UNESCAPED = lv_decoded_url.

WRITE: 'Encoded URL: ', lv_encoded_url, /, 'Decoded URL: ', lv_decoded_url.</lang>

Ada

Library: AWS

<lang Ada>with AWS.URL; with Ada.Text_IO; use Ada.Text_IO; procedure Decode is

  Encoded : constant String := "http%3A%2F%2Ffoo%20bar%2F";

begin

  Put_Line (AWS.URL.Decode (Encoded));

end Decode; </lang>

Without external libraries:

<lang Ada>package URL is

  function Decode (URL : in String) return String;

end URL;</lang>

<lang Ada>package body URL is

  function Decode (URL : in String) return String is
     Buffer   : String (1 .. URL'Length);
     Filled   : Natural := 0;
     Position : Positive := URL'First;
  begin
     while Position in URL'Range loop
        Filled := Filled + 1;

       case URL (Position) is
           when '+' =>
              Buffer (Filled) := ' ';
              Position := Position + 1;
           when '%' =>
              Buffer (Filled) :=
                Character'Val
                  (Natural'Value
                     ("16#" & URL (Position + 1 .. Position + 2) & "#"));
              Position := Position + 3;
           when others =>
              Buffer (Filled) := URL (Position);
              Position := Position + 1;
        end case;
     end loop;
     return Buffer (1 .. Filled);
  end Decode;

end URL;</lang>

<lang Ada>with Ada.Command_Line,

    Ada.Text_IO;

with URL;

procedure Test_URL_Decode is

  use Ada.Command_Line, Ada.Text_IO;

begin

  if Argument_Count = 0 then
     Put_Line (URL.Decode ("http%3A%2F%2Ffoo%20bar%2F"));
  else
     for I in 1 .. Argument_Count loop
        Put_Line (URL.Decode (Argument (I)));
     end loop;
  end if;

end Test_URL_Decode;</lang>

Apex

<lang apex>EncodingUtil.urlDecode('http%3A%2F%2Ffoo%20bar%2F', 'UTF-8'); EncodingUtil.urlDecode('google.com/search?q=%60Abdu%27l-Bah%C3%A1', 'UTF-8');</lang>

http://foo bar/
google.com/search?q=`Abdu'l-Bahá

AppleScript

<lang AppleScript>AST URL decode "google.com/search?q=%60Abdu%27l-Bah%C3%A1"</lang>

Arturo

<lang arturo>url1: "http%3A%2F%2Ffoo%20bar%2F" print [decodeUrl url1]

url2: "google.com/search?q=%60Abdu%27l-Bah%C3%A1" print [decodeUrl url2]</lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

AutoHotkey

<lang AutoHotkey>encURL := "http%3A%2F%2Ffoo%20bar%2F" SetFormat, Integer, hex Loop Parse, encURL

  If A_LoopField = `%
     reading := 2, read := ""
  else if reading
  {
     read .= A_LoopField, --reading
     if not reading
        out .= Chr("0x" . read)
  }
  else out .= A_LoopField

MsgBox % out ; http://foo bar/ </lang>

AWK

<lang AWK>

  1. syntax:

awk ' BEGIN {

   str = "http%3A%2F%2Ffoo%20bar%2F" # "http://foo bar/"
   printf("%s\n",str)
   len=length(str)
   for (i=1;i<=len;i++) {
     if ( substr(str,i,1) == "%") {
       L = substr(str,1,i-1) # chars to left of "%"
       M = substr(str,i+1,2) # 2 chars to right of "%"
       R = substr(str,i+3)   # chars to right of "%xx"
       str = sprintf("%s%c%s",L,hex2dec(M),R)
     }
   }
   printf("%s\n",str)
   exit(0)

} function hex2dec(s, num) {

   num = index("0123456789ABCDEF",toupper(substr(s,length(s)))) - 1
   sub(/.$/,"",s)
   return num + (length(s) ? 16*hex2dec(s) : 0)

} ' </lang>

Output:
http%3A%2F%2Ffoo%20bar%2F
http://foo bar/

BaCon

<lang freebasic>FUNCTION Url_Decode$(url$)

   LOCAL result$
   SPLIT url$ BY "%" TO item$ SIZE total
   FOR x = 1 TO total-1
       result$ = result$ & CHR$(DEC(LEFT$(item$[x], 2))) & MID$(item$[x], 3)
   NEXT
   RETURN item$[0] & result$

END FUNCTION

PRINT Url_Decode$("http%3A%2F%2Ffoo%20bar%2F") PRINT Url_Decode$("google.com/search?q=%60Abdu%27l-Bah%C3%A1")</lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

BBC BASIC

<lang bbcbasic> PRINT FNurldecode("http%3A%2F%2Ffoo%20bar%2F")

     END
     
     DEF FNurldecode(url$)
     LOCAL i%
     REPEAT
       i% = INSTR(url$, "%", i%+1)
       IF i% THEN
         url$ = LEFT$(url$,i%-1) + \
         \      CHR$EVAL("&"+FNupper(MID$(url$,i%+1,2))) + \
         \      MID$(url$,i%+3)
       ENDIF
     UNTIL i% = 0
     = url$
     
     DEF FNupper(A$)
     LOCAL A%,C%
     FOR A% = 1 TO LEN(A$)
       C% = ASCMID$(A$,A%)
       IF C% >= 97 IF C% <= 122 MID$(A$,A%,1) = CHR$(C%-32)
     NEXT
     = A$</lang>
Output:
http://foo bar/

Bracmat

<lang bracmat>( ( decode

     =   decoded hexcode notencoded
       .   :?decoded
         &   whl
           ' ( @(!arg:?notencoded "%" (% %:?hexcode) ?arg)
             & !decoded !notencoded chr$(x2d$!hexcode):?decoded
             )
         & str$(!decoded !arg)
     )
   & out$(decode$http%3A%2F%2Ffoo%20bar%2F)

);</lang>

Output:
http://foo bar/

C

<lang c>#include <stdio.h>

  1. include <string.h>

inline int ishex(int x) { return (x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'); }

int decode(const char *s, char *dec) { char *o; const char *end = s + strlen(s); int c;

for (o = dec; s <= end; o++) { c = *s++; if (c == '+') c = ' '; else if (c == '%' && ( !ishex(*s++) || !ishex(*s++) || !sscanf(s - 2, "%2x", &c))) return -1;

if (dec) *o = c; }

return o - dec; }

int main() { const char *url = "http%3A%2F%2ffoo+bar%2fabcd"; char out[strlen(url) + 1];

printf("length: %d\n", decode(url, 0)); puts(decode(url, out) < 0 ? "bad string" : out);

return 0; }</lang>

C#

<lang csharp>using System;

namespace URLEncode {

   internal class Program
   {
       private static void Main(string[] args)
       {
           Console.WriteLine(Decode("http%3A%2F%2Ffoo%20bar%2F"));
       }
       private static string Decode(string uri)
       {
           return Uri.UnescapeDataString(uri);
       }
   }

}</lang>

Output:
http://foo bar/

C++

Library: Poco
Works with: g++

<lang cpp>#include <string>

  1. include "Poco/URI.h"
  2. include <iostream>

int main( ) {

  std::string encoded( "http%3A%2F%2Ffoo%20bar%2F" ) ;
  std::string decoded ;
  Poco::URI::decode ( encoded , decoded ) ;
  std::cout << encoded << " is decoded: " << decoded << " !" << std::endl ;
  return 0 ;

}</lang>

Output:
http%3A%2F%2Ffoo%20bar%2F is decoded: http://foo bar/ !

Caché ObjectScript

USER>Write $ZConvert("http%3A%2F%2Ffoo%20bar%2F", "I", "URL")
http://foo bar/

Clojure

<lang clojure>(java.net.URLDecoder/decode "http%3A%2F%2Ffoo%20bar%2F")</lang>

CoffeeScript

<lang coffeescript> console.log decodeURIComponent "http%3A%2F%2Ffoo%20bar%2F?name=Foo%20Barson" </lang>

<lang> > coffee foo.coffee http://foo bar/?name=Foo Barson </lang>

Common Lisp

<lang lisp>(defun decode (string &key start)

 (assert (char= (char string start) #\%))
 (if (>= (length string) (+ start 3))
     (multiple-value-bind (code pos)
         (parse-integer string :start (1+ start) :end (+ start 3) :radix 16 :junk-allowed t)
       (if (= pos (+ start 3))
           (values (code-char code) pos)
           (values #\% (1+ start))))
     (values #\% (1+ start))))

(defun url-decode (url)

 (loop with start = 0
       for pos = (position #\% url :start start)
       collect (subseq url start pos) into chunks
       when pos
         collect (multiple-value-bind (decoded next) (decode url :start pos)
                   (setf start next)
                   (string decoded))
           into chunks
       while pos
       finally (return (apply #'concatenate 'string chunks))))

(url-decode "http%3A%2F%2Ffoo%20bar%2F")</lang>

Output:
"http://foo bar/"

D

<lang d>import std.stdio, std.uri;

void main() {

   writeln(decodeComponent("http%3A%2F%2Ffoo%20bar%2F"));

}</lang>

http://foo bar/

Delphi

<lang Delphi>program URLEncoding;

{$APPTYPE CONSOLE}

uses IdURI;

begin

 Writeln(TIdURI.URLDecode('http%3A%2F%2Ffoo%20bar%2F'));

end.</lang>

Elixir

<lang elixir>IO.inspect URI.decode("http%3A%2F%2Ffoo%20bar%2F") IO.inspect URI.decode("google.com/search?q=%60Abdu%27l-Bah%C3%A1")</lang>

Output:
"http://foo bar/"
"google.com/search?q=`Abdu'l-Bahá"

Erlang

Built in.

34> http_uri:decode("http%3A%2F%2Ffoo%20bar%2F").
"http://foo bar/"

F#

Translation of: C#

<lang fsharp>open System

let decode uri = Uri.UnescapeDataString(uri)

[<EntryPoint>] let main argv =

   printfn "%s" (decode "http%3A%2F%2Ffoo%20bar%2F")
   0</lang>

Factor

<lang factor>USING: io kernel urls.encoding ; IN: rosetta-code.url-decoding

"http%3A%2F%2Ffoo%20bar%2F" "google.com/search?q=%60Abdu%27l-Bah%C3%A1" [ url-decode print ] bi@</lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

Free Pascal

<lang pascal>function urlDecode(data: String): AnsiString; var

 ch: Char;
 pos, skip: Integer;

begin

 pos := 0;
 skip := 0;
 Result := ;
 for ch in data do begin
   if skip = 0 then begin
     if (ch = '%') and (pos < data.length -2) then begin
       skip := 2;
       Result := Result + AnsiChar(Hex2Dec('$' + data[pos+2] + data[pos+3]));
     end else begin
       Result := Result + ch;
     end;
   end else begin
     skip := skip - 1;
   end;
   pos := pos +1;
 end;

end;</lang>

Go

<lang go>package main

import ( "fmt" "log" "net/url" )

func main() { for _, escaped := range []string{ "http%3A%2F%2Ffoo%20bar%2F", "google.com/search?q=%60Abdu%27l-Bah%C3%A1", } { u, err := url.QueryUnescape(escaped) if err != nil { log.Println(err) continue } fmt.Println(u) } }</lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

Groovy

<lang groovy>assert URLDecoder.decode('http%3A%2F%2Ffoo%20bar%2F') == 'http://foo bar/'</lang>

Haskell

<lang Haskell>import qualified Data.Char as Char

urlDecode :: String -> Maybe String urlDecode [] = Just [] urlDecode ('%':xs) =

 case xs of
   (a:b:xss) ->
     urlDecode xss
     >>= return . ((Char.chr . read $ "0x" ++ [a,b]) :)
   _ -> Nothing

urlDecode ('+':xs) = urlDecode xs >>= return . (' ' :) urlDecode (x:xs) = urlDecode xs >>= return . (x :)

main :: IO () main = putStrLn . maybe "Bad decode" id $ urlDecode "http%3A%2F%2Ffoo%20bar%2F"</lang>

Output:
http://foo bar/

Another approach: <lang haskell>import Data.Char (chr) import Data.List.Split (splitOn)

deCode :: String -> String deCode url =

 let ps = splitOn "%" url
 in concat $
    head ps :
    ((\(a, b) -> (chr . read) (mappend "0x" a) : b) <$> (splitAt 2 <$> tail ps))

-- TEST ------------------------------------------------------------------------ main :: IO () main = putStrLn $ deCode "http%3A%2F%2Ffoo%20bar%2F"</lang>

Output:
http://foo bar/

Icon and Unicon

<lang Icon>link hexcvt

procedure main() ue := "http%3A%2F%2Ffoo%20bar%2F" ud := decodeURL(ue) | stop("Improperly encoded string ",image(ue)) write("encoded = ",image(ue)) write("decoded = ",image(ue)) end

procedure decodeURL(s) #: decode URL/URI encoded data static de initial { # build lookup table for everything

 de := table()
 every de[hexstring(ord(c := !string(&ascii)),2)] := c
 }

c := "" s ? until pos(0) do # decode every %xx or fail

  c ||:= if ="%" then \de[move(2)] | fail
  else move(1)

return c end</lang>

hexcvt provides hexstring

Output:
encoded = "http%3A%2F%2Ffoo%20bar%2F"
decoded = "http://foo bar/"

J

J does not have a native urldecode (until version 7 when the jhs ide addon includes a jurldecode).

Here is an implementation:

<lang j>require'strings convert' urldecode=: rplc&(~.,/;"_1&a."2(,:tolower)'%',.toupper hfd i.#a.)</lang>

Example use:

<lang j> urldecode 'http%3A%2F%2Ffoo%20bar%2F' http://foo bar/</lang>

Note that an earlier implementation assumed the j6 implementation of hfd which where hexadecimal letters resulting from hfd were upper case. J8, in contrast, provides a lower case result from hfd. The addition of toupper guarantees the case insensitivity required by RFC 3986 regardless of which version of J you are using. As the parenthesized expression containing hfd is only evaluated at definition time, there's no performance penalty from the use of toupper.

Example use:

<lang j> urldecode 'google.com/search?q=%60Abdu%27l-Bah%C3%A1' google.com/search?q=`Abdu'l-Bahá</lang>

Java

<lang java>import java.io.UnsupportedEncodingException; import java.net.URLDecoder;

public class Main {

   public static void main(String[] args) throws UnsupportedEncodingException
   {
       String encoded = "http%3A%2F%2Ffoo%20bar%2F";
       String normal = URLDecoder.decode(encoded, "utf-8");
       System.out.println(normal);
   }

}</lang>

Output:
http://foo bar/

JavaScript

<lang javascript>decodeURIComponent("http%3A%2F%2Ffoo%20bar%2F")</lang>

jq

Works with: jq version 1.4

If your jq already has "until", then the definition given below may be omitted. <lang jq># Emit . and stop as soon as "condition" is true. def until(condition; next):

 def u: if condition then . else (next|u) end;
 u;

def url_decode:

 # The helper function converts the input string written in the given
 # "base" to an integer
 def to_i(base):
   explode
   | reverse
   | map(if 65 <= . and . <= 90 then . + 32  else . end)   # downcase
   | map(if . > 96  then . - 87 else . - 48 end)  # "a" ~ 97 => 10 ~ 87
   | reduce .[] as $c
       # base: [power, ans]
       ([1,0]; (.[0] * base) as $b | [$b, .[1] + (.[0] * $c)]) | .[1];
 .  as $in
 | length as $length
 | [0, ""]  # i, answer
 | until ( .[0] >= $length;
     .[0] as $i
     |  if $in[$i:$i+1] == "%"
        then [ $i + 3, .[1] + ([$in[$i+1:$i+3] | to_i(16)] | implode) ]
        else [ $i + 1, .[1] + $in[$i:$i+1] ]
        end)
 | .[1];  # answer</lang>

Example: <lang jq>"http%3A%2F%2Ffoo%20bar%2F" | url_decode</lang>

Output:

<lang sh> "http://foo bar/"</lang>

Julia

<lang Julia> using URIParser

enc = "http%3A%2F%2Ffoo%20bar%2F"

dcd = unescape(enc)

println(enc, " => ", dcd) </lang>

Output:
http%3A%2F%2Ffoo%20bar%2F => http://foo bar/

Kotlin

<lang scala>// version 1.1.2

import java.net.URLDecoder

fun main(args: Array<String>) {

   val encoded = arrayOf("http%3A%2F%2Ffoo%20bar%2F", "google.com/search?q=%60Abdu%27l-Bah%C3%A1")
   for (e in encoded) println(URLDecoder.decode(e, "UTF-8"))

}</lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

Lambdatalk

Currently lambdatalk has no builtin primitive for decoding URLs. Let's define it using Javascript. <lang scheme> 1) define a new javascript primitive: {script

 LAMBDATALK.DICT['decodeURIComponent'] = function() {
   return decodeURIComponent( arguments[0].trim() );
 };

}

2) and use it: {decodeURIComponent http%3A%2F%2Ffoo%20bar%2F} -> http://foo bar/


</lang>

Lasso

<lang Lasso>bytes('http%3A%2F%2Ffoo%20bar%2F') -> decodeurl</lang> -> http://foo bar/

Liberty BASIC

<lang lb>

   dim lookUp$( 256)
   for i =0 to 256
       lookUp$( i) ="%" +dechex$( i)
   next i
   url$ ="http%3A%2F%2Ffoo%20bar%2F"
   print "Supplied URL '"; url$; "'"
   print "As string    '"; url2string$( url$); "'"
   end

function url2string$( i$)

   for j =1 to len( i$)
       c$ =mid$( i$, j, 1)
       if c$ ="%" then
           nc$         =chr$( hexdec( mid$( i$, j +1, 2)))
           url2string$ =url2string$ +nc$ 
           j =j +2
       else
           url2string$ =url2string$ +c$
       end if
   next j

end function </lang>

Supplied URL 'http%3A%2F%2Ffoo%20bar%2F'
As string 'http://foo bar/'

Lingo

<lang Lingo>---------------------------------------- -- URL decodes a string -- @param {string} str -- @return {string}


on urldecode (str)

   res = ""
   ba = bytearray()
   len = str.length
   repeat with i = 1 to len
       c = str.char[i]
       if (c = "%") then
           -- fastest hex-to-dec conversion hack based on Lingo's rgb object
           ba.writeInt8(rgb(str.char[i+1..i+2]).blue)
           i = i + 2
       else if (c = "+") then
           ba.writeInt8(32)
       else
           ba.writeInt8(chartonum(c))
       end if
   end repeat
   ba.position = 1
   return ba.readRawString(ba.length)

end</lang> <lang Lingo>put urldecode("http%3A%2F%2Ffoo%20bar%2F") put urldecode("google.com/search?q=%60Abdu%27l-Bah%C3%A1")</lang>

Output:
-- "http://foo bar/"
-- "google.com/search?q=`Abdu'l-Bahá"

LiveCode

<lang LiveCode>put urlDecode("http%3A%2F%2Ffoo%20bar%2F") & cr & \

   urlDecode("google.com/search?q=%60Abdu%27l-Bah%C3%A1")</lang>Results<lang>http://foo bar/

google.com/search?q=`Abdu'l-Bah√°</lang>

Lua

<lang lua>function decodeChar(hex) return string.char(tonumber(hex,16)) end

function decodeString(str) local output, t = string.gsub(str,"%%(%x%x)",decodeChar) return output end

-- will print "http://foo bar/" print(decodeString("http%3A%2F%2Ffoo%20bar%2F"))</lang>

M2000 Interpreter

Function Len(string) return length in words, so a value 1.5 means 3 bytes

We can add strings with half word at the end of a series of words.

A$=str$("A") has a length of 0.5

b$=chr$(a$) revert bytes to words adding zeroes after each character <lang M2000 Interpreter> Module CheckIt {

     Function decodeUrl$(a$) {
           DIM a$()
           a$()=Piece$(a$, "%")
           if len(a$())=1 then =str$(a$):exit
           k=each(a$(),2)
           \\ convert to one byte per character using str$(string)
           acc$=str$(a$(0))
           While k {
                       \\ chr$() convert to UTF16LE 
                       \\ str$()  convert to ANSI using locale (can be 1033 we can set it before as Locale 1033)
                       \\ so chr$(0x93) give 0x201C
                       \\ str$(chr$(0x93)) return one byte 93 in ANSI as string of one byte length
                       \\ numbers are for UTF-8 so we have to preserve them
                 acc$+=str$(Chr$(Eval("0x"+left$(a$(k^),2)))+Mid$(a$(k^),3))
           }
           =acc$
     }
     \\ decode from utf8
     final$=DecodeUrl$("google.com/search?q=%60Abdu%27l-Bah%C3%A1")
     Print string$(final$ as utf8dec)="google.com/search?q=`Abdu'l-Bahá"
     final$=DecodeUrl$("http%3A%2F%2Ffoo%20bar%2F")
     Print string$(final$ as utf8dec)="http://foo bar/"

} CheckIt </lang>

Maple

<lang Maple>StringTools:-Decode("http%3A%2F%2Ffoo%20bar%2F", encoding=percent);</lang>

Output:
                           "http://foo bar/"

Mathematica

<lang mathematica>URLDecoding[url_] :=

StringReplace[url, "%" ~~ x_ ~~ y_ :> FromDigits[x ~~ y, 16]] //. 
 StringExpression[x___, Longest[n__Integer], y___] :> 
  StringExpression[x, FromCharacterCode[{n}, "UTF8"], y]</lang>

Example use:

<lang mathematica>URLDecoding["http%3A%2F%2Ffoo%20bar%2F"]</lang>

Output:
http://foo bar/

MATLAB / Octave

<lang MATLAB>function u = urldecoding(s)

   u = ;
   k = 1;
   while k<=length(s)
       if s(k) == '%' && k+2 <= length(s)
           u = sprintf('%s%c', u, char(hex2dec(s((k+1):(k+2)))));
           k = k + 3;
       else
           u = sprintf('%s%c', u, s(k));
           k = k + 1;
       end        
   end

end</lang> Usage:

octave:3> urldecoding('http%3A%2F%2Ffoo%20bar%2F')
ans = http://foo bar/

NetRexx

<lang NetRexx>/* NetRexx */ options replace format comments java crossref savelog symbols nobinary

url = [ -

 'http%3A%2F%2Ffoo%20bar%2F', -
 'mailto%3A%22Ivan%20Aim%22%20%3Civan%2Eaim%40email%2Ecom%3E', -
 '%6D%61%69%6C%74%6F%3A%22%49%72%6D%61%20%55%73%65%72%22%20%3C%69%72%6D%61%2E%75%73%65%72%40%6D%61%69%6C%2E%63%6F%6D%3E' -
 ]

loop u_ = 0 to url.length - 1

 say url[u_]
 say DecodeURL(url[u_])
 say
 end u_

return

method DecodeURL(arg) public static

 Parse arg encoded
 decoded = 
 PCT = '%'
 loop label e_ while encoded.length() > 0
   parse encoded head (PCT) +1 code +2 tail
   decoded = decoded || head
   select
     when code.strip('T').length() = 2 & code.datatype('X') then do
       code = code.x2c()
       decoded = decoded || code
       end
     when code.strip('T').length() \= 0 then do
       decoded = decoded || PCT
       tail = code || tail
       end
     otherwise do
       nop
       end
     end
   encoded = tail
   end e_
 return decoded

</lang>

Output:
http%3A%2F%2Ffoo%20bar%2F
http://foo bar/

mailto%3A%22Ivan%20Aim%22%20%3Civan%2Eaim%40email%2Ecom%3E
mailto:"Ivan Aim" <ivan.aim@email.com>

%6D%61%69%6C%74%6F%3A%22%49%72%6D%61%20%55%73%65%72%22%20%3C%69%72%6D%61%2E%75%73%65%72%40%6D%61%69%6C%2E%63%6F%6D%3E
mailto:"Irma User" <irma.user@mail.com>

NewLISP

<lang NewLISP>;; universal decoder, works for ASCII and UTF-8

(source http://www.newlisp.org/index.cgi?page=Code_Snippets)

(define (url-decode url (opt nil))

 (if opt (replace "+" url " "))
 (replace "%([0-9a-f][0-9a-f])" url (pack "b" (int $1 0 16)) 1))

(url-decode "http%3A%2F%2Ffoo%20bar%2F")</lang>

Nim

<lang nim>import cgi

echo decodeUrl("http%3A%2F%2Ffoo%20bar%2F")</lang>

Oberon-2

Works with: oo2c

<lang oberon2> MODULE URLDecoding; IMPORT

 URI := URI:String,
 Out := NPCT:Console;

BEGIN

 Out.String(URI.Unescape("http%3A%2F%2Ffoo%20bar%2F"));Out.Ln;
 Out.String(URI.Unescape("google.com/search?q=%60Abdu%27l-Bah%C3%A1"));Out.Ln;  

END URLDecoding. </lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

Objeck

<lang objeck> class UrlDecode {

 function : Main(args : String[]) ~ Nil {
   Net.UrlUtility->Decode("http%3A%2F%2Ffoo%20bar%2F")->PrintLine();
 }

} </lang>

Objective-C

<lang objc>NSString *encoded = @"http%3A%2F%2Ffoo%20bar%2F"; NSString *normal = [encoded stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSLog(@"%@", normal);</lang>

Works with: Mac OS X version 10.9+
Works with: iOS version 7+

<lang objc>NSString *encoded = @"http%3A%2F%2Ffoo%20bar%2F"; NSString *normal = [encoded stringByRemovingPercentEncoding]; NSLog(@"%@", normal);</lang>

OCaml

Using the library ocamlnet from the interactive loop:

<lang ocaml>$ ocaml

  1. #use "topfind";;
  2. #require "netstring";;
  1. Netencoding.Url.decode "http%3A%2F%2Ffoo%20bar%2F" ;;

- : string = "http://foo bar/"</lang>

ooRexx

While the implementation shown for Rexx will also work with ooRexx, this version uses ooRexx syntax to invoke the built-in functions. <lang ooRexx>/* Rexx */

 X = 0
 url. = 
 X = X + 1; url.0 = X; url.X = 'http%3A%2F%2Ffoo%20bar%2F'
 X = X + 1; url.0 = X; url.X = 'mailto%3A%22Ivan%20Aim%22%20%3Civan%2Eaim%40email%2Ecom%3E'
 X = X + 1; url.0 = X; url.X = '%6D%61%69%6C%74%6F%3A%22%49%72%6D%61%20%55%73%65%72%22%20%3C%69%72%6D%61%2E%75%73%65%72%40%6D%61%69%6C%2E%63%6F%6D%3E'
 Do u_ = 1 to url.0
   Say url.u_
   Say DecodeURL(url.u_)
   Say
   End u_

Exit

DecodeURL: Procedure

 Parse Arg encoded
 decoded = 
 PCT = '%'
 Do label e_ while encoded~length() > 0
   Parse Var encoded head (PCT) +1 code +2 tail
   decoded = decoded || head
   Select
     when code~strip('T')~length() = 2 & code~datatype('X') then Do
       code = code~x2c()
       decoded = decoded || code
       End
     when code~strip('T')~length() \= 0 then Do
       decoded = decoded || PCT
       tail = code || tail
       End
     otherwise 
       Nop
     End
   encoded = tail
   End e_
 Return decoded</lang>
Output:
http%3A%2F%2Ffoo%20bar%2F
http://foo bar/

mailto%3A%22Ivan%20Aim%22%20%3Civan%2Eaim%40email%2Ecom%3E
mailto:"Ivan Aim" <ivan.aim@email.com>

%6D%61%69%6C%74%6F%3A%22%49%72%6D%61%20%55%73%65%72%22%20%3C%69%72%6D%61%2E%75%73%65%72%40%6D%61%69%6C%2E%63%6F%6D%3E
mailto:"Irma User" <irma.user@mail.com>

Perl

<lang Perl>sub urldecode {

   my $s = shift;
   $s =~ tr/\+/ /;
   $s =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/eg;
   return $s;

}

print urldecode('http%3A%2F%2Ffoo+bar%2F')."\n"; </lang>

<lang Perl>#!/usr/bin/perl -w use strict ; use URI::Escape ;

my $encoded = "http%3A%2F%2Ffoo%20bar%2F" ; my $unencoded = uri_unescape( $encoded ) ; print "The unencoded string is $unencoded !\n" ;</lang>

Phix

<lang Phix>-- -- demo\rosetta\decode_url.exw -- =========================== -- function decode_url(string s) integer skip = 0 string res = ""

   for i=1 to length(s) do
       if skip then
           skip -= 1
       else
           integer ch = s[i]
           if ch='%' then
               sequence scanres = {}
               if i+2<=length(s) then
                   scanres = scanf("#"&s[i+1..i+2],"%x")
               end if
               if length(scanres)!=1 then
                   return "decode error"
               end if
               skip = 2
               ch = scanres[1][1]
           elsif ch='+' then
               ch = ' '
           end if
           res &= ch
       end if
   end for
   return res

end function

printf(1,"%s\n",{decode_url("http%3A%2F%2Ffoo%20bar%2F")}) printf(1,"%s\n",{decode_url("google.com/search?q=%60Abdu%27l-Bah%C3%A1")})</lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

PHP

<lang php><?php $encoded = "http%3A%2F%2Ffoo%20bar%2F"; $unencoded = rawurldecode($encoded); echo "The unencoded string is $unencoded !\n"; ?></lang>

PicoLisp

: (ht:Pack (chop "http%3A%2F%2Ffoo%20bar%2F") T)
-> "http://foo bar/"

Pike

<lang Pike> void main() {

   array encoded_urls = ({
       "http%3A%2F%2Ffoo%20bar%2F",
       "google.com/search?q=%60Abdu%27l-Bah%C3%A1"
   });


   foreach(encoded_urls, string url) {
       string decoded = Protocols.HTTP.uri_decode( url );
       write( string_to_utf8(decoded) +"\n" ); // Assume sink does UTF8
   }

} </lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

PowerShell

<lang PowerShell> [System.Web.HttpUtility]::UrlDecode("http%3A%2F%2Ffoo%20bar%2F") </lang>

Output:
http://foo bar/

PureBasic

<lang PureBasic>URL$ = URLDecoder("http%3A%2F%2Ffoo%20bar%2F")

Debug URL$  ; http://foo bar/</lang>

Python

<lang Python>

  1. Python 2.X

import urllib print urllib.unquote("http%3A%2F%2Ffoo%20bar%2F")

  1. Python 3.5+

from urllib.parse import unquote print(unquote('http%3A%2F%2Ffoo%20bar%2F')) </lang>

R

<lang R>URLdecode("http%3A%2F%2Ffoo%20bar%2F")</lang>

Racket

<lang racket>

  1. lang racket

(require net/uri-codec) (uri-decode "http%3A%2F%2Ffoo%20bar%2F") </lang>

Raku

(formerly Perl 6) <lang perl6>my $url = "http%3A%2F%2Ffoo%20bar%2F";

say $url.subst: :g,

   /'%'(<:hexdigit>**2)/,
   ->  ($ord          ) { chr(:16(~$ord)) }
  1. Alternately, you can use an in-place substitution:

$url ~~ s:g[ '%' (<:hexdigit> ** 2) ] = chr :16(~$0); say $url;</lang>

Retro

This is provided by the casket library (used for web app development).

<lang Retro>create buffer 32000 allot

{{

 create bit 5 allot
 : extract  ( $c-$a ) drop @+ bit ! @+ bit 1+ ! bit ;
 : render   ( $c-$n )
   dup '+ = [ drop 32 ] ifTrue
   dup 13 = [ drop 32 ] ifTrue
   dup 10 = [ drop 32 ] ifTrue
   dup '% = [ extract hex toNumber decimal ] ifTrue ;
 : <decode> (  $-$  ) repeat @+ 0; render ^buffer'add again ;

---reveal---

 : decode   (  $-   ) buffer ^buffer'set <decode> drop ;

}}

"http%3A%2F%2Ffoo%20bar%2F" decode buffer puts</lang>

REXX

version 1

Translation of: ooRexx

Tested with the ooRexx and Regina interpreters. <lang REXX>/* Rexx */

Do

 X = 0
 url. = 
 X = X + 1; url.0 = X; url.X = 'http%3A%2F%2Ffoo%20bar%2F'
 X = X + 1; url.0 = X; url.X = 'mailto%3A%22Ivan%20Aim%22%20%3Civan%2Eaim%40email%2Ecom%3E'
 X = X + 1; url.0 = X; url.X = '%6D%61%69%6C%74%6F%3A%22%49%72%6D%61%20%55%73%65%72%22%20%3C%69%72%6D%61%2E%75%73%65%72%40%6D%61%69%6C%2E%63%6F%6D%3E'
 Do u_ = 1 to url.0
   Say url.u_
   Say DecodeURL(url.u_)
   Say
   End u_
 Return

End Exit

DecodeURL: Procedure Do

 Parse Arg encoded
 decoded = 
 PCT = '%'
 Do while length(encoded) > 0
   Parse Var encoded head (PCT) +1 code +2 tail
   decoded = decoded || head
   Select
     When length(strip(code, 'T')) = 2 & datatype(code, 'X') then Do
       code = x2c(code)
       decoded = decoded || code
       End
     When length(strip(code, 'T')) \= 0 then Do
       decoded = decoded || PCT
       tail = code || tail
       End
     Otherwise Do
       Nop
       End
     End
   encoded = tail
   End
 Return decoded

End Exit </lang>

Output:
http%3A%2F%2Ffoo%20bar%2F
http://foo bar/

mailto%3A%22Ivan%20Aim%22%20%3Civan%2Eaim%40email%2Ecom%3E
mailto:"Ivan Aim" <ivan.aim@email.com>

%6D%61%69%6C%74%6F%3A%22%49%72%6D%61%20%55%73%65%72%22%20%3C%69%72%6D%61%2E%75%73%65%72%40%6D%61%69%6C%2E%63%6F%6D%3E
mailto:"Irma User" <irma.user@mail.com>

version 2

This REXX version is identical to version 1, but with superfluous and dead code removed. <lang REXX>/*REXX program converts a URL─encoded string ──► its original unencoded form. */ url.1='http%3A%2F%2Ffoo%20bar%2F' url.2='mailto%3A%22Ivan%20Aim%22%20%3Civan%2Eaim%40email%2Ecom%3E' url.3='%6D%61%69%6C%74%6F%3A%22%49%72%6D%61%20%55%73%65%72%22%20%3C%69%72%6D%61%2E%75%73%65%72%40%6D%61%69%6C%2E%63%6F%6D%3E' URLs =3

           do j=1  for URLs
           say url.j
           say decodeURL(url.j)
           say
           end   /*j*/

exit /*──────────────────────────────────────────────────────────────────────────────────────*/ decodeURL: procedure; parse arg encoded; decoded=

             do  while encoded\==
             parse var encoded   head  '%'  +1  code  +2  tail
             decoded= decoded || head
             L= length( strip( code, 'T') )
                select
                when L==2 & datatype(code, "X")  then       decoded= decoded || x2c(code)
                when L\==0                       then do;   decoded= decoded'%'
                                                            tail= code || tail
                                                      end
                otherwise nop
                end    /*select*/
             encoded= tail
             end   /*while*/
           return decoded</lang>
output   is identical to the 1st REXX version.

version 3

This REXX version is a shorter version of version 2. <lang REXX>/*REXX program converts & displays a URL─encoded string ──► its original unencoded form.*/ url. = url.1='http%3A%2F%2Ffoo%20bar%2F' url.2='mailto%3A%22Ivan%20Aim%22%20%3Civan%2Eaim%40email%2Ecom%3E' url.3='%6D%61%69%6C%74%6F%3A%22%49%72%6D%61%20%55%73%65%72%22%20%3C%69%72%6D%61%2E%75%73%65%72%40%6D%61%69%6C%2E%63%6F%6D%3E'

           do j=1  until url.j==;   say       /*process each URL; display blank line.*/
           say           url.j                  /*display the original URL.            */
           say URLdecode(url.j)                 /*   "     "  decoded   "              */
           end   /*j*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ URLdecode: procedure; parse arg yyy /*get encoded URL from argument list. */

           yyy= translate(yyy, , '+')           /*a special case for an encoded blank. */
           URL=
                         do  until yyy==
                         parse var  yyy     plain  '%'  +1  code  +2  yyy
                         URL= URL || plain
                         if datatype(code, 'X')  then URL= URL || x2c(code)
                                                 else URL= URL'%'code
                         end   /*until*/
           return URL</lang>
output   is identical to the 1st REXX version.

Ruby

Use any one of CGI.unescape or URI.decode_www_form_component. These methods also convert "+" to " ".

<lang ruby>require 'cgi' puts CGI.unescape("http%3A%2F%2Ffoo%20bar%2F")

  1. => "http://foo bar/"</lang>
Works with: Ruby version 1.9.2

<lang ruby>require 'uri' puts URI.decode_www_form_component("http%3A%2F%2Ffoo%20bar%2F")

  1. => "http://foo bar/"</lang>

URI.unescape (alias URI.unencode) still works. URI.unescape is obsolete since Ruby 1.9.2 because of problems with its sibling URI.escape.

Rust

<lang rust>const INPUT1: &str = "http%3A%2F%2Ffoo%20bar%2F"; const INPUT2: &str = "google.com/search?q=%60Abdu%27l-Bah%C3%A1";

fn append_frag(text: &mut String, frag: &mut String) {

   if !frag.is_empty() {
       let encoded = frag.chars()
           .collect::<Vec<char>>()
           .chunks(2)
           .map(|ch| {
               u8::from_str_radix(&ch.iter().collect::<String>(), 16).unwrap()
           }).collect::<Vec<u8>>();
       text.push_str(&std::str::from_utf8(&encoded).unwrap());
       frag.clear();
   }

}

fn decode(text: &str) -> String {

   let mut output = String::new();
   let mut encoded_ch = String::new();
   let mut iter = text.chars();
   while let Some(ch) = iter.next() {
       if ch == '%' {
           encoded_ch.push_str(&format!("{}{}", iter.next().unwrap(), iter.next().unwrap()));
       } else {
           append_frag(&mut output, &mut encoded_ch);
           output.push(ch);
       }
   }
   append_frag(&mut output, &mut encoded_ch);
   output

}

fn main() {

   println!("{}", decode(INPUT1));
   println!("{}", decode(INPUT2));

}</lang>

Output:
http://foo bar/
google.com/search?q=`Abdu'l-Bahá

Scala

Library: Scala

<lang scala>import java.net.{URLDecoder, URLEncoder}

import scala.compat.Platform.currentTime

object UrlCoded extends App {

 val original = """http://foo bar/"""
 val encoded: String = URLEncoder.encode(original, "UTF-8")
 assert(encoded == "http%3A%2F%2Ffoo+bar%2F", s"Original: $original not properly encoded: $encoded")
 val percentEncoding = encoded.replace("+", "%20")
 assert(percentEncoding == "http%3A%2F%2Ffoo%20bar%2F", s"Original: $original not properly percent-encoded: $percentEncoding")
 assert(URLDecoder.decode(encoded, "UTF-8") == URLDecoder.decode(percentEncoding, "UTF-8"))
 println(s"Successfully completed without errors. [total ${currentTime - executionStart} ms]")

}</lang>

Seed7

The library encoding.s7i defines functions to handle URL respectively percent encoding. The function fromPercentEncoded decodes a percend-encoded string. The function fromUrlEncoded works like fromPercentEncoded and additionally decodes '+' with a space. Both functions return byte sequences. To decode Unicode characters it is necessary to convert them from UTF-8 with utf8ToStri afterwards. <lang seed7>$ include "seed7_05.s7i";

 include "encoding.s7i";

const proc: main is func

 begin
   writeln(fromPercentEncoded("http%3A%2F%2Ffoo%20bar%2F"));
   writeln(fromUrlEncoded("http%3A%2F%2Ffoo+bar%2F"));
 end func;</lang>
Output:
http://foo bar/
http://foo bar/

Sidef

Translation of: Perl

<lang ruby>func urldecode(str) {

   str.gsub!('+', ' ');
   str.gsub!(/\%([A-Fa-f0-9]{2})/, {|a| 'C'.pack(a.hex)});
   return str;

}

say urldecode('http%3A%2F%2Ffoo+bar%2F'); # => "http://foo bar/"</lang>

Swift

<lang swift>import Foundation

let encoded = "http%3A%2F%2Ffoo%20bar%2F" if let normal = encoded.stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {

 println(normal)

}</lang>

Tcl

This code is careful to ensure that any untoward metacharacters in the input string still do not cause any problems. <lang tcl>proc urlDecode {str} {

   set specialMap {"[" "%5B" "]" "%5D"}
   set seqRE {%([0-9a-fA-F]{2})}
   set replacement {[format "%c" [scan "\1" "%2x"]]}
   set modStr [regsub -all $seqRE [string map $specialMap $str] $replacement]
   return [encoding convertfrom utf-8 [subst -nobackslash -novariable $modStr]]

}</lang> Demonstrating: <lang tcl>puts [urlDecode "http%3A%2F%2Ffoo%20bar%2F"]</lang>

Output:
http://foo bar/

TUSCRIPT

<lang tuscript> $$ MODE TUSCRIPT url_encoded="http%3A%2F%2Ffoo%20bar%2F" BUILD S_TABLE hex=":%><:><2<>2<%:" hex=STRINGS (url_encoded,hex), hex=SPLIT(hex) hex=DECODE (hex,hex) url_decoded=SUBSTITUTE(url_encoded,":%><2<>2<%:",0,0,hex) PRINT "encoded: ", url_encoded PRINT "decoded: ", url_decoded </lang>

Output:
encoded: http%3A%2F%2Ffoo%20bar%2F
decoded: http://foo bar/

UNIX Shell

Works with: bash
Works with: ksh

<lang bash>urldecode() { local u="${1//+/ }"; printf '%b' "${u//%/\\x}"; }</lang>

Example:

<lang bash>urldecode http%3A%2F%2Ffoo%20bar%2F http://foo bar/

urldecode google.com/search?q=%60Abdu%27l-Bah%C3%A1 google.com/search?q=`Abdu'l-Bahároot@ </lang>


<lang bash>function urldecode {

       typeset encoded=$1 decoded= rest= c= c1= c2=
       typeset rest2= bug='rest2=${rest}'
       if [[ -z ${BASH_VERSION:-} ]]; then
               typeset -i16 hex=0; typeset -i8 oct=0
               # bug /usr/bin/sh HP-UX 11.00
               typeset _encoded='xyz%26xyz'
               rest="${_encoded#?}"
               c="${_encoded%%${rest}}"
               if (( ${#c} != 1 )); then
                       typeset qm='????????????????????????????????????????????????????????????????????????'
                       typeset bug='(( ${#rest} > 0 )) && typeset -L${#rest} rest2="${qm}" || rest2=${rest}'
               fi
       fi

rest="${encoded#?}" eval ${bug} c="${encoded%%${rest2}}" encoded="${rest}"

while [[ -n ${c} ]]; do if [[ ${c} = '%' ]]; then rest="${encoded#?}" eval ${bug} c1="${encoded%%${rest2}}" encoded="${rest}"

rest="${encoded#?}" eval ${bug} c2="${encoded%%${rest2}}" encoded="${rest}"

if [[ -z ${c1} || -z ${c2} ]]; then c="%${c1}${c2}" echo "WARNING: invalid % encoding: ${c}" >&2 elif [[ -n ${BASH_VERSION:-} ]]; then c="\\x${c1}${c2}" c=$(\echo -e "${c}") else hex="16#${c1}${c2}"; oct=hex c="\\0${oct#8\#}" c=$(print -- "${c}") fi elif [[ ${c} = '+' ]]; then c=' ' fi

decoded="${decoded}${c}"

rest="${encoded#?}" eval ${bug} c="${encoded%%${rest2}}" encoded="${rest}" done

if [[ -n ${BASH_VERSION:-} ]]; then \echo -E "${decoded}" else print -r -- "${decoded}" fi } </lang>

VBScript

<lang VBScript>Function RegExTest(str,patrn)

   Dim regEx
   Set regEx = New RegExp
   regEx.IgnoreCase = True
   regEx.Pattern = patrn
   RegExTest = regEx.Test(str)

End Function

Function URLDecode(sStr)

   Dim str,code,a0
   str=""
   code=sStr
   code=Replace(code,"+"," ")
   While len(code)>0
       If InStr(code,"%")>0 Then
           str = str & Mid(code,1,InStr(code,"%")-1)
           code = Mid(code,InStr(code,"%"))
           a0 = UCase(Mid(code,2,1))
           If a0="U" And RegExTest(code,"^%u[0-9A-F]{4}") Then
               str = str & ChrW((Int("&H" & Mid(code,3,4))))
               code = Mid(code,7)
           ElseIf a0="E" And RegExTest(code,"^(%[0-9A-F]{2}){3}") Then
               str = str & ChrW((Int("&H" & Mid(code,2,2)) And 15) * 4096 + (Int("&H" & Mid(code,5,2)) And 63) * 64 + (Int("&H" & Mid(code,8,2)) And 63))
               code = Mid(code,10)
           ElseIf a0>="C" And a0<="D" And RegExTest(code,"^(%[0-9A-F]{2}){2}") Then
               str = str & ChrW((Int("&H" & Mid(code,2,2)) And 3) * 64 + (Int("&H" & Mid(code,5,2)) And 63))
               code = Mid(code,7)
           ElseIf (a0<="B" Or a0="F") And RegExTest(code,"^%[0-9A-F]{2}") Then
               str = str & Chr(Int("&H" & Mid(code,2,2)))
               code = Mid(code,4)
           Else
               str = str & "%"
               code = Mid(code,2)
           End If
       Else
           str = str & code
           code = ""
       End If
   Wend
   URLDecode = str

End Function

url = "http%3A%2F%2Ffoo%20bar%C3%A8%2F" WScript.Echo "Encoded URL: " & url & vbCrLf &_ "Decoded URL: " & UrlDecode(url)</lang>

Output:
Encoded URL: http%3A%2F%2Ffoo%20bar%C3%A8%2F
Decoded URL: http://foo barè/

XPL0

<lang XPL0>code Text=12; string 0; \use zero-terminated strings

func Decode(S0); \Decode URL string and return its address char S0; char S1(80); \BEWARE: very temporary string space returned int C, N, I, J; [I:= 0; J:= 0; repeat C:= S0(I); I:= I+1; \get char

       if C=^% then                            \convert hex to char
               [C:= S0(I);  I:= I+1;
               if C>=^a then C:= C & ~$20;     \convert to uppercase
               N:= C - (if C<=^9 then ^0 else ^A-10);
               C:= S0(I);  I:= I+1;
               if C>=^a then C:= C & ~$20;
               C:= N*16 + C - (if C<=^9 then ^0 else ^A-10);
               ];
       S1(J):= C;  J:= J+1;                    \put char in output string

until C=0; return S1; ];

Text(0, Decode("http%3A%2F%2Ffoo%20bar%2f"))</lang>

Output:
http://foo bar/

Yabasic

Translation of: Phix

<lang Yabasic>sub decode_url$(s$)

   local res$, ch$
   while(s$ <> "")
       ch$ = left$(s$, 1)
       if ch$ = "%" then
           ch$ = chr$(dec(mid$(s$, 2, 2)))
           s$ = right$(s$, len(s$) - 3)
       else
           if ch$ = "+" ch$ = " "
           s$ = right$(s$, len(s$) - 1)

endif

       res$ = res$ + ch$
   wend
   return res$

end sub

print decode_url$("http%3A%2F%2Ffoo%20bar%2F") print decode_url$("google.com/search?q=%60Abdu%27l-Bah%C3%A1")</lang>

zkl

<lang zkl>"http%3A%2F%2Ffoo%20bar%2F".pump(String, // push each char through these fcns:

  fcn(c){ if(c=="%") return(Void.Read,2); return(Void.Skip,c) },// %-->read 2 chars else pass through
  fcn(_,b,c){ (b+c).toInt(16).toChar() })  // "%" (ignored)  "3"+"1"-->0x31-->"1"</lang>
Output:
http://foo bar/

or use libCurl: <lang zkl>var Curl=Import.lib("zklCurl"); Curl.urlDecode("http%3A%2F%2Ffoo%20bar%2F");</lang>

Output:
http://foo bar/