24
\$\begingroup\$

Write a program that takes as its input a string consisting of printable characters (ASCII 20-7E) and an integer n in [2,16] and performs the following modification to the string.

  • Each character in the string is converted to its ASCII code (the examples given are in hexadecimal, though base 10 is also acceptable).
  • The ASCII codes are converted to base n and are concatenated together.
  • The new string is split every other character. If there are an odd number of characters, then the last character is removed entirely.
  • Printing ASCII codes (in base 16) are converted back into their characters, whereas non-printing ASCII codes are removed.
  • The resulting string is printed.

Test case

Input

Hello, World!
6

Steps

Hello, World!
48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21
2002453003003031125222330331030024453
20 02 45 30 03 00 30 31 12 52 22 33 03 31 03 00 24 45

The output of this program is E001R"31$E.


This is code golf, so standard rules apply. Shortest code in bytes wins.

\$\endgroup\$
7
  • \$\begingroup\$ This encoding algorithm can be useful for sending secret messages! \$\endgroup\$
    – user41805
    Commented Nov 4, 2015 at 20:02
  • \$\begingroup\$ Gotta do this sometime! \$\endgroup\$ Commented Nov 4, 2015 at 20:49
  • \$\begingroup\$ @ΚριτικσιΛίθος while the program works for every possible input string, not every output string is unique. For example, in base 7, the string J would go through the steps J -> 50 -> 101 -> 10 -> (no output), as would the string K or L. \$\endgroup\$
    – Arcturus
    Commented Nov 4, 2015 at 21:29
  • \$\begingroup\$ Like @Eridan said, this is a lossy encryption since odd sequences get the last character lopped off. Though I'm sure to the ignorant observer it could be a snarky way of communicating :) \$\endgroup\$ Commented Nov 4, 2015 at 21:33
  • 1
    \$\begingroup\$ Step 1 is confusing - no need to convert chars to hexadecimal - in the example: H is ASCII 72 (decimal) or 48 (hex), but what I need is 200 (base 6). All the row 2 in the example is useless and confusing in my opinion \$\endgroup\$
    – edc65
    Commented Nov 4, 2015 at 23:23

14 Answers 14

5
\$\begingroup\$

Pyth - 22 bytes

Hope to golf a lot more, pretty straightforward.

sfq3l`TmCid16csjRQCMz2

Try it online here.

\$\endgroup\$
2
  • \$\begingroup\$ q3l`T is wonderful. \$\endgroup\$
    – isaacg
    Commented Nov 5, 2015 at 2:48
  • \$\begingroup\$ Shouldn't fgTd be enough? \$\endgroup\$
    – Jakube
    Commented Nov 5, 2015 at 9:41
4
\$\begingroup\$

CJam, 33 bytes

q~:i\fb:~0+2/W<Gfb:c' ,-'ÿ),127>-

Takes input in the form 6 "Hello, World!". Test it here.

See Dennis's answer for a similar but better solution with a nice explanation.

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

Bash + common linux utils, 118

printf %s "$1"|xxd -p|sed -r "s/../\U& /g;y/ /n/;s/^/dc -e$2o16i/e;s/../& /g;s/ .$//;"|xxd -rp|sed 's/[^[:print:]]//g'
\$\endgroup\$
3
  • \$\begingroup\$ I think you could shorten printf %s "$1" into echo -n "$1" to save 2 bytes \$\endgroup\$
    – Aaron
    Commented Nov 5, 2015 at 9:44
  • \$\begingroup\$ @Aaron That works until the input string is -e. Try echo -n "-e" \$\endgroup\$ Commented Nov 5, 2015 at 19:06
  • \$\begingroup\$ Damn, nicely spotted ! \$\endgroup\$
    – Aaron
    Commented Nov 5, 2015 at 19:20
2
\$\begingroup\$

CJam, 24 bytes

l:irifbe_2/{Gbc',32>&}/

Note that there is a DEL character (0x7F) between ' and ,. Try it online in the CJam interpreter.

How it works

l:i                     Read a line from STDIN and cast each char to integer. 
   ri                   Read another integer (base) from STDIN.
     fb                 Convert each integer from line 1 to that base.
       e_2/             Flatten and split into chunks of length 2.
                        If the last chunk has only one element, it will get
                        converted into a control character, which will be
                        removed later.
          {         }/  For each digit pair:
           Gb             Convert the pair from base 16 to integer.
             c            Cast to character.
              ',          Push the string of ASCII characters up to '~'.
                32>       Remove the first 32 (control characters).
                   &      Intersect.
\$\endgroup\$
7
  • \$\begingroup\$ What about the DEL character...? It looks like you've worked around that, but I can't see it in your explanation! \$\endgroup\$
    – wizzwizz4
    Commented Nov 5, 2015 at 19:29
  • \$\begingroup\$ StackExchange filters unprintable characters. The DEL character is only present in thr permalink, and invisible even there. \$\endgroup\$
    – Dennis
    Commented Nov 5, 2015 at 19:35
  • \$\begingroup\$ I mean... Delete is a control character, but is not one of the first 32 characters. It is character number 127, 0x7F, for people who are not familiar with ASCII. \$\endgroup\$
    – wizzwizz4
    Commented Nov 8, 2015 at 16:35
  • \$\begingroup\$ I'm not sure I understood your question. Are you wondering how I filter it from the output? \$\endgroup\$
    – Dennis
    Commented Nov 8, 2015 at 16:55
  • \$\begingroup\$ Yes. Your code explanation doesn't seem to say how you filter the DEL character from the output. \$\endgroup\$
    – wizzwizz4
    Commented Nov 10, 2015 at 19:15
2
\$\begingroup\$

JavaScript (ES6), 137 147

Using the most verbose functions available in JavaScript

f=(s,b)=>alert(s.replace(/./g,x=>x.charCodeAt().toString(b)).match(/../g).map(x=>(x=String.fromCharCode('0x'+x))<='~'&x>' '?x:'').join``)

// Just for test purpose, redefine alert()
alert=x=>document.write('<pre>'+x+'</pre>')

f('Hello, World!',6)
f('PORK',3)

\$\endgroup\$
5
  • \$\begingroup\$ I think you can save some bytes by using [for(z of ...)if(...)...] instead of map(...).filter(...) \$\endgroup\$
    – Ypnypn
    Commented Nov 4, 2015 at 23:58
  • \$\begingroup\$ @Ypnypn I did not found a way to use your hint (apart from using array comprehension that is ES7) but you pressed me to rethink it all. Thx. I hope you'll keep your +1 even if x=>x>= has gone \$\endgroup\$
    – edc65
    Commented Nov 5, 2015 at 0:08
  • 1
    \$\begingroup\$ What's wrong with using ES7? \$\endgroup\$
    – Ypnypn
    Commented Nov 5, 2015 at 1:20
  • 1
    \$\begingroup\$ @Ypnypn I prefer an answer that can work even with subpar javascript engines <troll on>like Chrome</troll off> \$\endgroup\$
    – edc65
    Commented Nov 5, 2015 at 6:09
  • \$\begingroup\$ @edc65 +1 because Chrome is subpar! (Please don't lynch me internet!) But why have you opened <troll on> then closed <troll off>? \$\endgroup\$
    – wizzwizz4
    Commented Nov 5, 2015 at 19:32
1
\$\begingroup\$

Julia, 118 bytes

f(s,n)=join(map(i->(c=string(Char(parse(Int,i,16))))^isprint(c),matchall(r"..",join(map(i->base(n,Int(i)),[s...])))))

Ungolfed:

function f(s::AbstractString, n::Integer)
    # Construct an array of ASCII codes in base n
    v = map(i -> base(n, Int(i)), [s...])

    # Join into a string and get all pairs, truncating
    # to an even length
    m = matchall(r"..", join(v))

    # Parse each pair as an integer in base 16, get the
    # character associated with that code point, convert
    # to a string, and include if it's printable
    x = map(i -> (c = string(Char(parse(Int, i, 16)))^isprint(c), m)

    # Join as a string and return
    return join(x)
end
\$\endgroup\$
1
\$\begingroup\$

Mathematica, 134 bytes

Print@FromCharacterCode@Select[#~FromDigits~16&/@StringPartition[""<>ToCharacterCode@InputString[]~IntegerString~Input[],2],31<#<127&]

If a function is allowed:

Mathematica, 112 bytes

FromCharacterCode@Select[#~FromDigits~16&/@StringPartition[""<>ToCharacterCode@#~IntegerString~#2,2],31<#<127&]&
\$\endgroup\$
1
\$\begingroup\$

TeaScript, 23 bytes

TeaScript is JavaScript for golfing

£lc¡T(y©K(2)ßC(P(l,16±µ

Relatively straight-forward but delightfully short. I can probably golf down a few more characters with some more operators. A few other new features might also be able to be used to cut down some bytes.

Ungolfed && Explanation

x.l(#
    l.c().T(y)
).K(2)
.m(#
    C(
      P(l,16)
    )
).j``
\$\endgroup\$
2
  • 1
    \$\begingroup\$ I believe this is 23 chars (29 bytes) long. \$\endgroup\$ Commented Nov 5, 2015 at 12:10
  • \$\begingroup\$ @w0lf That would be with UTF-8 encoding but because all characters are less then 256 we can safely count them as one byte \$\endgroup\$
    – Downgoat
    Commented Nov 5, 2015 at 15:19
1
\$\begingroup\$

Ruby 92

->s,n{o=''
s.chars.map{|x|x.ord.to_s n}.join.scan(/../).map{|x|x>?2&&x<?8&&o<<x.to_i(16)}
o}

Online test here.

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

Python 2, 174 bytes

def J(a,b,i=0):
 h=r=''
 B=lambda n,b:n*'x'and B(n/b,b)+chr(48+n%b+7*(n%b>9))
 for c in a:h+=B(ord(c),b)
 while i<len(h):v=int(h[i:i+2],16);r+=chr(v)*(31<v<127);i+=2
 print r

Try it here

Not really the best tool for the job. Since Python has no convert-to-arbitrary-base function, I had to implement my own. That was fun, at least--particularly finding a [marginally] shorter expression for the digits than "0123456789ABCDEF"[n%b]. For iterating over two characters at a time, I found a while loop was slightly shorter than a functional approach.

181 bytes as a full program:

B=lambda n,b:n*'x'and B(n/b,b)+chr(48+n%b+7*(n%b>9))
a=raw_input()
b=input()
h=r=''
for c in a:h+=B(ord(c),b)
i=0
while i<len(h):v=int(h[i:i+2],16);r+=chr(v)*(31<v<127);i+=2
print r
\$\endgroup\$
1
\$\begingroup\$

Vyxal s, 13 bytes

C$vτHfṅǒHCkQ↔

Try it Online!

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

MATLAB, 103 bytes

function k(s,n),b=dec2base(s,n)';b(~cumsum(b-'0',1))='';c=base2dec(textscan(b,'%2c'),16)';char(c(c>31))

I've written a function k that takes a string s and an integer n as input. e.g.:

k('Hello, World!',6)

gives

 E001R"31$E

Most annoying thing I had to work around is leading zeros showing up when converting to base n. Getting these out of the array that was to be split after every 2nd character cost quite a lot of bytes. Not sure if it is possible to save any more bytes using this approach.

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

PHP - 286 bytes

Put the string in $s and the integer in $b.

<?php $s=$_GET["s"];$b;$m="array_map";echo implode($m(function($v){return ctype_print($v)?$v:"";},$m("chr",$m("hexdec",str_split(strlen(implode($a=$m(function($v){global$b;return base_convert($v,16,$b);},$m("dechex",$m("ord",str_split($s))))))%2==1?substr(implode($a),0,-1):$a,2)))));?>

Pass the value to GET["s"].

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

Ly, 26 bytes

ir[sp>lS<]>ry[puu' oy1G*]p

Try it online!

This code uses the S instruction to convert the codepoints to digits one input character at a time. Then it just prints pairs of digits delimited with spaces from the accumulation stack.

ir
i                          - read input as codepoints
 r                         - reverse stack
  [      ]                 - for each input char/codepoint...
   sp                      - stash codepoint and delete
     >l                    - switch to accum stack, load codepoint
       S                   - convert codepoint to digits
        <                  - switch back to input stack
          >r               - switch to accum stack, reverse digits
            y[          ]  - loop until stack size is <2
              p            - delete loop iterator artifact
               uu          - print two digits
                 ' o       - print a space
                    y1G    - is the stack size >1?
                       *   - multiple to combine top two of stack
                         p - print iterator artifact
\$\endgroup\$

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