17
\$\begingroup\$

Given two strings as input, return the result of XORing the code-points of one string against the code points of the other.

For each character in the first input string, take the code-point (e.g. for A, this is 65) and XOR the value against the corresponding index in the second string and output the character at the code-point of the result. If one string is longer than the other, you must return the portion of the string beyond the length of the shorter, as-is. (Alternatively, you may pad the shorter string with NUL bytes, which is equivalent.)

See the following JavaScript code for an example:

const xorStrings = (a, b) => {
  let s = '';

  // use the longer of the two words to calculate the length of the result
  for (let i = 0; i < Math.max(a.length, b.length); i++) {
    // append the result of the char from the code-point that results from
    // XORing the char codes (or 0 if one string is too short)
    s += String.fromCharCode(
      (a.charCodeAt(i) || 0) ^ (b.charCodeAt(i) || 0)
    );
  }

  return s;
};

Try it online!

Test cases

Input                         Output

['Hello,', 'World!']          '\x1f\x0a\x1e\x00\x0b\x0d'
['Hello', 'wORLD']            '?*> +'
['abcde', '01234']            'QSQWQ'
['lowercase', "9?'      "]    'UPPERCASE'
['test', '']                  'test'
['12345', '98765']            '\x08\x0a\x04\x02\x00' _not_ 111092
['test', 'test']              '\x00\x00\x00\x00'
['123', 'ABCDE']              'pppDE'
['01', 'qsCDE']               'ABCDE'
['`c345', 'QQ']               '12345'

Rules

  • The two input strings will only ever be code-points 0-255.
  • This is so the shortest solution, in each language, wins.
\$\endgroup\$
  • \$\begingroup\$ Are the two strings limited to ASCII? \$\endgroup\$ – Bubbler 2 days ago
  • 2
    \$\begingroup\$ You might want to add a few other test cases of varying length besides just ['test', '']. I see many answers being posted which fail for this test case due to the length difference (or because one of the two is empty maybe). \$\endgroup\$ – Kevin Cruijssen 2 days ago
  • 1
    \$\begingroup\$ @KevinCruijssen Done and emboldened (and hopefully added a viable alternative) the detail. \$\endgroup\$ – Dom Hastings 2 days ago
  • 1
    \$\begingroup\$ Alternatively, padding the shorter string with NUL bytes is equivalent. Can you confirm that you mean we can, if we like, assume that the two strings will be of the same length with the originally shorter one padded with NUL characters? Or does this mean that the implementation should do the padding, and that this is equivalent to the requirement to "return the portion of the string beyond the length of the shorter, as-is"? \$\endgroup\$ – user7761803 2 days ago
  • 3
    \$\begingroup\$ @user7761803 I've edited to clarify that I meant it should be padded as part of your answer. Hopefully it's clear now! \$\endgroup\$ – Dom Hastings 2 days ago

18 Answers 18

7
\$\begingroup\$

Jelly, 4 bytes

O^/Ọ

Try it online!

Takes input as a list of the two strings, e.g. ['abcde', '01234'].

How?

O    # ord: cast to number (automatically vectorizes)
 ^/  # Reduce by XOR. XOR automatically applies to corresponding elements
         and pads as desired to work if the two strings are different lengths
   Ọ # chr: cast to character (vectorizes once again)
| improve this answer | |
\$\endgroup\$
7
\$\begingroup\$

Raku, 4 bytes

*~^*

Try it online!

Raku has a built-in operator for XORing strings, along with string AND, OR and bitshift. This is a Whatever lambda that takes two parameters.

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

perl -Mfeature=say,bitwise -nl, 22 bytes

$.%2?($;=$_):say$;^.$_

Try it online!

This is way more characters than I first hoped for. If it weren't for those pesky newlines, the 9 character say<>^.<> would do.

How does it work?

For odd input lines, it saves the current line of input (without the trailing newline due to the -n and -l switches) into $;. For even lines, it xors the previous line ($;) and the current line ($_), and prints it. The ^. operator does required bitwise string operation.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I didn't even know about -Mbitwise, but I think you can drop it (and the .). For single inputs (which is fine IMO), your intended idea should work too: Try it online! \$\endgroup\$ – Dom Hastings 2 days ago
  • 2
    \$\begingroup\$ @DomHastings Yeah, but that relies on having the last line of STDIN to not be terminated with a newline, which is kind of icky (and hard to spot when looking at test input). BTW, it's not -Mbitwise, but -Mfeature=bitwise. \$\endgroup\$ – Abigail 2 days ago
  • \$\begingroup\$ That's very true, it does! Without the -Mfeature=bitwise though, you can avoid the . in ^. for -1! \$\endgroup\$ – Dom Hastings 2 days ago
  • 2
    \$\begingroup\$ When testing, I was running the program as perl -M5.032 -nl program.pl < input. This turns on strict, which prohibits an undeclared $x (or any other single letter variable). $; however is always a package variable. \$\endgroup\$ – Abigail 2 days ago
  • 1
    \$\begingroup\$ @Abigail The challenge specifies input is in the range 0-255. So I think you have to account for newlines in the strings. Maybe do it as a subroutine instead of a standalone script (is that still legal here?), something like pop^.pop? \$\endgroup\$ – msh210 17 hours ago
5
\$\begingroup\$

APL (Dyalog Unicode), 15 bytes

80⎕DR≠⌿↑11⎕DR¨⎕

Try it online!

As the OP clarified that the input codepoints will be in the range of 0-255, it is possible to manipulate the underlying data bits directly. Such a string is guaranteed to have data type 80 (8-bit char array), so we convert it to data type 11 (1-bit boolean array) to access the bits, XOR them, and convert back to data type 80.

80⎕DR≠⌿↑11⎕DR¨⎕  ⍝ Full program, input: two string literals on a line
        11⎕DR¨⎕  ⍝ Convert each string literal to bit array
       ↑         ⍝ Promote to matrix, padding with 0 as needed
     ≠⌿          ⍝ Bitwise XOR
80⎕DR            ⍝ Convert back to 8-bit char array

APL (Dyalog Extended), 17 bytes

⎕UCS⊥≠⌿⍤2⊤↑⎕UCS¨⎕

Try it online!

Well, the task involves converting char to charcode and back AND converting from/to binary, but all current implementations having have some quirks so it can't be used here. So here is the very literal implementation of the task.

⎕UCS⊥≠⌿⍤2⊤↑⎕UCS¨⎕  ⍝ Full program, input: two string literals on one line
           ⎕UCS¨⎕  ⍝ Convert to codepoints
          ↑        ⍝ Promote into a 2-row matrix, padding zeros as necessary
                   ⍝ (doing on characters give spaces which is 0x20, not 0)
         ⊤  ⍝ Convert each number to binary
     ≠⌿⍤2   ⍝ Bitwise XOR
    ⊥       ⍝ Convert the binary back to integers
⎕UCS        ⍝ Convert the integers back to chars
| improve this answer | |
\$\endgroup\$
5
\$\begingroup\$

J, 14 bytes

XOR@,:&.(3&u:)

Try it online!

How it works

XOR@,:&.(3&u:)
        (3&u:) strings -> code points
      &.       do right part, then left part, then the inverse of the right part
    ,:         pad shorter one with zeros by making a table
XOR@           XOR the code points
        (3&u:) revert back code points -> string
| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

05AB1E, 8 7 bytes

thanks to Kevin Cruijssen for a byte!

Ç0ζε`^ç

Try it online!

Commented

	 implicit input          ["QQ", "`c345"]

Ç        convert to charcodes    [[96, 99, 51, 52, 53], [81, 81]]
  ζ      Zip with filler ...     [[96, 81], [99, 81], [51, "0"], [52, "0"], [53, "0"]]
 0       ... zero
   ε     Map ...                   [96, 81]
    `      Dump on stack           96, 81
     ^     XOR                     49
      ç    Convert to character    "1"

         implicit output         ["1", "2", "3", "4", "5"]
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 7 bytes by taking the input as a pair of strings. \$\endgroup\$ – Kevin Cruijssen 2 days ago
4
\$\begingroup\$

C (gcc), 60 58 56 bytes

Saved 2 4 bytes thanks to AZTECCO!!!

#define f(a,b)for(;*a+*b;a+=!!*a,b+=!!*b)putchar(*a^*b);

Try it online!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ @AZTECCO Unfortunately, that doesn't print the end of the longer string. But found a way that does - thanks! :-) \$\endgroup\$ – Noodle9 yesterday
  • \$\begingroup\$ Oh my bad, you can save 1 more btw Try it online! \$\endgroup\$ – AZTECCO 23 hours ago
  • \$\begingroup\$ And one more using macros Try it o \$\endgroup\$ – AZTECCO 23 hours ago
  • \$\begingroup\$ @AZTECCO Nice one - thanks! :-) \$\endgroup\$ – Noodle9 16 hours ago
3
\$\begingroup\$

Factor, 48 bytes

: f ( s s -- s ) 0 pad-longest [ bitxor ] 2map ;

Try it online!

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

Excel, 158 bytes

Compatibility Notes:

  • Tested on Office Online because I don't have Excel at home. Seems it requires closing parens. Once I, or someone else can verify that this works on real Excel without the closing parens, I'll update it.
  • Minimum version: CONCAT() came to be in later versions of Excel 2016 (from CONCATENATE()).

The Formulae

  • Inputs: A1, B1
  • A2: =MIN(LEN(A1:B1)), 16
  • B2: =LEN(A1)-LEN(B1), 16

Code (126):

=CONCAT(CHAR(BITXOR(CODE(MID(A1,ROW(OFFSET(A1,,,A2)),1)),CODE(MID(B1,ROW(OFFSET(A1,,,A2)),1)))))&RIGHT(IF(B2>0,A1,B1),ABS(B2))

One unfortunate caveat is that Excel ignores non-printable characters in cells. Alternatively, if you'd rather I use "\xXX" characters, I have this:

=CONCAT("\x"&DEC2HEX(BITXOR(CODE(MID(A1,ROW(OFFSET(A1,,,A2)),1)),CODE(MID(B1,ROW(OFFSET(A1,,,A2)),1))),2))&RIGHT(IF(B2>0,A1,B1),ABS(B2))

at 136 bytes. This just prints all XOR'ed characters as "\xXX" characters and leaves the trailing characters alone. Eg: Hello! and World!! produce \x3F\x2A\x3E\x20\x2B\x00!

How it Works:

  1. The ROW(OFFSET(A1,,,A2)) first creates an artificial cell range starting in A1 of height A2 (Length of the shortest string), then it fills that range with the row number. This effectively creates a range of (1..A2). As far as I can tell, I cannot re-use this by caching it in a cell, which is why I had to use it twice.
  2. They are then converted to numbers with CODE() and BITXOR()ed against each other.
  3. The CHAR() converts this to a character, while DEC2HEX(...,2) converts it to a 2 width 0-padded hex number.
  4. CONCAT() puts the array together
  5. RIGHT(...) tacks on the trailing characters of the longer string.
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ May want to throw in that as this uses the BitXOr function, this is limited to Excel 2013 and later \$\endgroup\$ – Taylor Scott 23 hours ago
  • 1
    \$\begingroup\$ @TaylorScott True, but I think the minimum compatibility is actually 2019, because CONCATENATE() was changed to CONCAT() \$\endgroup\$ – Calculuswhiz 23 hours ago
  • \$\begingroup\$ You're right - Concat() is the limiting function here, but I think it limits to Office v 16.0.xx which would include Office 2016, 2019 and 365. At least I can confirm that it is functional in up-to date versions of Excel 2016 on Win 10 \$\endgroup\$ – Taylor Scott 22 hours ago
  • 1
    \$\begingroup\$ Yup, you're right. support.microsoft.com/en-us/office/… \$\endgroup\$ – Calculuswhiz 21 hours ago
2
\$\begingroup\$

Java 10, 109 bytes

(a,b)->{int A=a.length,B=b.length;if(A<B){var t=a;a=b;b=t;A^=B^(B=A);}for(;A-->0;)a[A]^=A<B?b[A]:0;return a;}

I/O as arrays of characters.

Try it online.

Explanation:

(a,b)->{             // Input as 2 character arrays as parameters as well as return-type
  int A=a.length,    //  `A`: the length of the first array `a`
      B=b.length;    //  `B`: the length of the second array `b`
  if(A<B){           //  If the length of `a` is smaller than `b`:
    var t=a;a=b;b=t; //   Swap the arrays `a` and `b`
    A^=B^(B=A);}     //   And also swap the lengths `A` and `B`
                     //  (`a`/`A` is now the largest array, and `b`/`B` the smallest)
  for(;A-->0;)       //  Loop index `A` in the range [`A`, 0):
    a[A]^=           //   Bitwise-XOR the `A`'th value in `a` with, and implicitly cast
                     //   from an integer codepoint to a character afterwards:
      A<B?           //    If index `A` is still within bounds for `b`:
       b[A]          //     XOR it with the `A`'th codepoint of `b`
      :              //    Else:
       0;            //     XOR it with 0 instead
  return a;}         //  Return the modified `a` as result

Note that we cannot use a currying lambda a->b-> here, because we modify the inputs when swapping and they should be (effectively) final for lambdas.

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

Japt, 6 bytes

cÈ^VcY

Try it

cÈ^VcY     :Implicit input of strings U & V
c          :Map the charcodes in U
 È         :by passing each one at index Y through the following function
  ^        :  Bitwise XOR with
   VcY     :  Charcode at index Y in V
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Charcoal, 33 bytes

F⌈EθLι«Fθ«≔ζη≔∧‹ιLκ℅§κιζ»℅⁻|ηζ&ηζ

Try it online! Link is to verbose version of code. Takes input as an array of two strings. Explanation:

F⌈EθLι«

Loop over the longer length of the strings.

Fθ«

Loop over the strings.

≔ζη

Save the result of the previous loop, if any.

≔∧‹ιLκ℅§κιζ

Get the ordinal at the current index, if that is less than the current string.

»℅⁻|ηζ&ηζ

Emulate bitwise XOR by subtracting the bitwise AND from the bitwise OR, then convert back to a character.

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

JavaScript (Node.js), 66 bytes

f=(a,b)=>b[a.length]?f(b,a):(B=Buffer)(a).map((c,i)=>c^B(b)[i])+''

Try it online!

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

Python 2, 80 72 69 bytes

lambda*a:''.join(map(lambda x,y:chr(ord(x or'\0')^ord(y or'\0')),*a))

Try it online!

Uneven lengths are annoying...

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

Scala, 64 bytes

(a,b)=>(""/:a.zipAll(b,'\0','\0').map(x=>x._1^x._2))(_+_.toChar)

Try it online!

Run with

val f: ((String,String)=>String) = ...
println(f("01","qsCDE"))
...

Uses zipAll to zip the input strings with null bytes as padding, then XORs, finally using foldLeft shorthand /: to turn the whole thing back into a string.

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

Python 3.8, 71 bytes

f=lambda a,b:chr(ord(a[0])^ord(b[0]))+f(a[1:],b[1:])if a and b else a+b

Try it online!

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

PowerShell, 86 bytes

$k=[char[]]($args[1]);$i=0;([byte[]]([char[]]($args[0])|%{$_-bxor$k[$i++%$k.Length]}))

Try it online!

| improve this answer | |
New contributor
Wasif_Hasan is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
  • \$\begingroup\$ Welcome. You can use $args[1]|% t*y instead [char[]]($args[1]). The % t*y means "call the method toCharArray. And you can omit $i=0. See also Tips for golfing in PowerShell \$\endgroup\$ – mazzy 14 hours ago
0
\$\begingroup\$

C (gcc), 44 bytes

x(o,r)char*o,*r;{*o|*r?*o^=*r,x(o+1,r+1):0;}

Try it online!

Uses recursion, note that to print strings containing the null byte one will have to manage the strings as arrays. (See the footer of the link for an example)

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You're relying on the first string storing as much memory as the second string in case the second string is longer than the first. That's not really playing fair. Certainly not how strings work in C. \$\endgroup\$ – Noodle9 7 mins ago

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.