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;
};
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 code-golf so the shortest solution, in each language, wins.
30 Answers 30
Jelly, 4 bytes
O^/Ọ
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)
perl -Mfeature=say,bitwise -nl, 22 bytes
$.%2?($;=$_):say$;^.$_
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.
-
\$\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– Dom Hastings2020年07月17日 11:10:49 +00:00Commented Jul 17, 2020 at 11:10 -
2\$\begingroup\$ @DomHastings Yeah, but that relies on having the last line of
STDINto 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– Abigail2020年07月17日 11:30:51 +00:00Commented Jul 17, 2020 at 11:30 -
\$\begingroup\$ That's very true, it does! Without the
-Mfeature=bitwisethough, you can avoid the.in^.for -1! \$\endgroup\$Dom Hastings– Dom Hastings2020年07月17日 12:39:59 +00:00Commented Jul 17, 2020 at 12:39 -
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– Abigail2020年07月17日 18:20:09 +00:00Commented Jul 17, 2020 at 18:20 -
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– msh2102020年07月19日 07:43:50 +00:00Commented Jul 19, 2020 at 7:43
APL (Dyalog Unicode), 15 bytes
80⎕DR≠⌿↑11⎕DR ̈⎕
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 ̈⎕
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
J, 14 bytes
XOR@,:&.(3&u:)
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
C (gcc), (削除) 60 (削除ここまで)\$\cdots\$ (削除) 55 (削除ここまで) 54 bytes
Saved (削除) 2 (削除ここまで) 4 bytes thanks to AZTECCO!!!
Saved (削除) a (削除ここまで) 2 bytes thanks to ceilingcat!!!
#define f(a,b)for(;*a+*b;b+=!!*b)a+=putchar(*a^*b)!=*b
-
\$\begingroup\$ @AZTECCO Unfortunately, that doesn't print the end of the longer string. But found a way that does - thanks! :-) \$\endgroup\$Noodle9– Noodle92020年07月18日 22:00:46 +00:00Commented Jul 18, 2020 at 22:00
-
\$\begingroup\$ Oh my bad, you can save 1 more btw Try it online! \$\endgroup\$AZTECCO– AZTECCO2020年07月19日 01:51:48 +00:00Commented Jul 19, 2020 at 1:51
-
-
\$\begingroup\$ @AZTECCO Nice one - thanks! :-) \$\endgroup\$Noodle9– Noodle92020年07月19日 09:21:35 +00:00Commented Jul 19, 2020 at 9:21
-
\$\begingroup\$ @ceilingcat Of course, nice one - thanks! :D \$\endgroup\$Noodle9– Noodle92020年07月20日 18:06:11 +00:00Commented Jul 20, 2020 at 18:06
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.
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.
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
-
2\$\begingroup\$ Nice solution! You can save 2 bytes by shortening the base case as
f=lambda a,b:a>''<b and chr(ord(a[0])^ord(b[0]))+f(a[1:],b[1:])or a+b\$\endgroup\$xnor– xnor2020年07月20日 04:13:03 +00:00Commented Jul 20, 2020 at 4:13 -
\$\begingroup\$
print(''.join(chr(ord(x)^ord(y))for x,y in zip(*input().split())))would be only 66 bytes but unfortunately, making it work on strings of different lengths would require many more bytes; and it doesn't handle strings that contain whitespace :-( \$\endgroup\$Stef– Stef2023年12月19日 16:22:41 +00:00Commented Dec 19, 2023 at 16:22
05AB1E, (削除) 8 (削除ここまで) 7 bytes
thanks to Kevin Cruijssen for a byte!
Ç0ζε`^ç
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"]
-
\$\begingroup\$ 7 bytes by taking the input as a pair of strings. \$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2020年07月17日 11:43:24 +00:00Commented Jul 17, 2020 at 11:43
Excel, (削除) 158 153 (削除ここまで) 108 bytes
(Not counting closing parens)
Compatibility Notes:
- Minimum version:
CONCAT()came to be in later versions of Excel 2016 (fromCONCATENATE()).
The Formulae
- Inputs:
A1,B1 - A2:
=MIN(LEN(A1:B1)), 14 - B2:
=LEN(A1)-LEN(B1), 15
Code (124):
=CONCAT(CHAR(BITXOR(CODE(MID(A1,SEQUENCE(A2),1)),CODE(MID(B1,SEQUENCE(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,SEQUENCE(A2),1)),CODE(MID(B1,SEQUENCE(A2),1))),2))&RIGHT(IF(B2>0,A1,B1),ABS(B2))
at 118 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:
- The
SEQUENCE(A2)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. - Each item is then converted to numbers with
CODE()andBITXOR()ed against each other. - The
CHAR()converts this to a character, whileDEC2HEX(...,2)converts it to a 2 width 0-padded hex number. CONCAT()puts the array togetherRIGHT(...)tacks on the trailing characters of the longer string.
-
1\$\begingroup\$ May want to throw in that as this uses the
BitXOrfunction, this is limited to Excel 2013 and later \$\endgroup\$Taylor Raine– Taylor Raine2020年07月19日 01:49:20 +00:00Commented Jul 19, 2020 at 1:49 -
1\$\begingroup\$ @TaylorScott True, but I think the minimum compatibility is actually 2019, because
CONCATENATE()was changed toCONCAT()\$\endgroup\$General Grievance– General Grievance2020年07月19日 02:10:04 +00:00Commented Jul 19, 2020 at 2:10 -
\$\begingroup\$ You're right -
Concat()is the limiting function here, but I think it limits to Office v16.0.xxwhich 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 Raine– Taylor Raine2020年07月19日 03:11:04 +00:00Commented Jul 19, 2020 at 3:11 -
1\$\begingroup\$ Yup, you're right. support.microsoft.com/en-us/office/… \$\endgroup\$General Grievance– General Grievance2020年07月19日 03:28:21 +00:00Commented Jul 19, 2020 at 3:28
PHP, 36 bytes
[,$a,$b]=$argv;echo"$a\r$b\r",$a^$b;
Usage:
$ php -r '[,$a,$b]=$argv;echo"$a\r$b\r",$a^$b;' -- 'ABCDE' '123';echo
> pppDE
Explanation: first output string A, then carriage return \r, output string B, then another carriage return, then output the XOR (which truncates to the shorter of the 2 strings). Any characters of the longer string will have already been printed.
PHP 7.4, 32 bytes
Using new arrow function syntax.
fn($a,$b)=>($a|$b^$b)^($a^$a|$b)
Explanation:
In PHP binary operators, only the | will keep the longest string length and pad with NULs. So we XOR string B with itself, leading to a string with NUL bytes of the same length as B, then OR that with A. This will pad A with NUL bytes and use the length of B, if B is longer than A. We do the same with B, and only then XOR.
Edits:
- arrow function variant
- missed the requirement of outputting the longest string
Vyxal, 4 bytes
C÷꘍C
Because Vyxal auto-pads lol.
Explained
C÷꘍C
C # convert each character in each string to it's ordinal value
÷꘍ # bitwise xor each item
C # and convert the result back to characters
Alternatively, using Keg mode:
Vyxal K, 1 byte
꘍
Uiua SBCS , (削除) 19 (削除ここまで) 12 bytes
⍜∩(⋯-@0円)⬚0≠
-7 thanks to Tbw
⍜∩(⋯-@0円)⬚0≠
⍜∩( ) # under both; call a function, then another, then invert the first on two things
-@0円 # convert string to code points
⋯ # convert to bits
⬚0≠ # are they not equal? (0 as default)
R, (削除) 136 (削除ここまで) 134 bytes
\(a,b){'+'=nchar;'?'=utf8ToInt;if(+a<+b){j=a;a=b;b=j};s=?a;names(s)=?b;n=names(s);n[is.na(n)]=0;intToUtf8(bitwXor(s,as.numeric(n)),T)}
- There is one little problem with the outputting of the
\x00symbol - it appears as an empty string"", therefore my output shown as a list of characters with the quotation marks.
JavaScript (Node.js), 66 bytes
f=(a,b)=>b[a.length]?f(b,a):(B=Buffer)(a).map((c,i)=>c^B(b)[i])+''
Scala, 64 bytes
(a,b)=>(""/:a.zipAll(b,'0円','0円').map(x=>x._1^x._2))(_+_.toChar)
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.
Japt, 6 bytes
cÈ^VcY
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
Lua, 105 bytes
i,a,b=0,...print(a:gsub('.',load'i=i+1return a.char((...):byte()~(b:sub(i,i):byte()or 0))')..b:sub(#a+1))
Taking two strings as arguments, this program calls per-character replace on one of them with essentially XOR function, then appends potentially missing fragment from second string (occurs if it is longer) and prints the result. TIO includes test suite.
Husk, 11 bytes
ż§oc-¤nc¤vc
Unfortunately Husk doesn't have a bitwise XOR command (that I could find), so we need to do:
arg1 OR (v) arg2 minus arg1 AND (n) arg2, costing an extra 5 bytes...
x86 machine code, 29 bytes
Machine code:
00000034: 31 d2 e3 08 8a 11 41 41 84 d2 e1 fe ac 84 c0 75 1.....AA.......u
00000044: 06 e3 09 89 ce 31 c9 30 d0 aa eb e4 c3 .....1.0.....
Commented assembly:
.intel_syntax noprefix
.globl .strxor
// Doesn't follow standard calling convention.
// Input:
// EDI: Output buffer large enough for longest string
// ESI, ECX: Null terminated strings to XOR
// Output:
// NON-null terminated string stored in EDI.
.strxor:
.Lloop:
// Always clear EDX.
xor edx, edx
// We mark the end of the shorter string
// by setting ECX to null.
jecxz .Lskip_ecx
.Lno_skip_ecx:
// Read a byte from ECX into DL.
mov dl, byte ptr [ecx]
// Increment twice because LOOPZ decrements.
inc ecx
inc ecx
// Was it '0円'?
test dl, dl
// If so, set ECX to NULL by using LOOPZ in place.
// CMOVZ ECX, EAX also works, but needs P6.
// This works with i386 and is the same size.
.Lclear_ecx:
loopz .Lclear_ecx
.Lskip_ecx:
// Load from ESI and autoincrement
lods al, byte ptr [esi]
// Test for '0円'
test al, al
jnz .Lno_swap
.Lswap: // '0円' found
// If ECX is null, we are at the end of both strings.
jecxz .Lend
// Otherwise, we swap ESI and ECX, and then clear ECX.
// Set ESI to ECX.
mov esi, ecx
// And set ECX to NULL.
xor ecx, ecx
// fallthrough
.Lno_swap:
// XOR the two bytes together
xor al, dl
// Store to EDI and autoincrement
stos byte ptr [edi], al
// Loop unconditionally.
jmp .Lloop
.Lend:
ret
The code reads two null terminated strings from esi and ecx, and stores the NON null terminated string in edi.
The logic is probably easier to see in the equivalent C code.
void strxor(char *dst, const char *src1, const char *src2)
{
for (;;) {
char x = '0円';
if (src2 != NULL) {
x = *src2++;
if (x == '0円') { // eos
src2 = NULL;
}
}
char y = *src1++;
if (y == '0円') {
if (src2 == NULL) { // both eos
return;
} else { // src2 is longer
src1 = src2;
src2 = NULL;
}
}
*dst++ = x ^ y;
}
}
I don't null terminate because null bytes naturally show up in the output and it saves a byte.
I may also add a version with explicit lengths which accepts null bytes, but the solution would be significantly less elegant.
Charcoal, 33 bytes
F⌈EθLι«Fθ«≔ζη≔∧‹ιLκc/o§κιζ»c/o−|ηζ&ηζ
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κc/o§κιζ
Get the ordinal at the current index, if that is less than the current string.
»c/o−|ηζ&ηζ
Emulate bitwise XOR by subtracting the bitwise AND from the bitwise OR, then convert back to a character.
PowerShell, (削除) 86 (削除ここまで) 81 bytes
$k=[char[]]($args[1]);([byte[]]([char[]]($args[0])|%{$_-bxor$k[$i++%$k.Length]}))
-5 bytes thanks to @mazzy
-
\$\begingroup\$ Welcome. You can use
$args[1]|% t*yinstead[char[]]($args[1]). The% t*ymeans "call the methodtoCharArray. And you can omit$i=0. See also Tips for golfing in PowerShell \$\endgroup\$mazzy– mazzy2020年07月19日 10:44:14 +00:00Commented Jul 19, 2020 at 10:44 -
\$\begingroup\$ Am I wrong or does this not work for two strings of unequal length? \$\endgroup\$stackprotector– stackprotector2020年07月26日 15:51:31 +00:00Commented Jul 26, 2020 at 15:51
-
\$\begingroup\$ @Thomas yep. You can see the two arguments are different length. \$\endgroup\$wasif– wasif2020年07月26日 15:54:46 +00:00Commented Jul 26, 2020 at 15:54
-
\$\begingroup\$ 1. You have to quote your input, if it contains spaces. You are actually processing
Iandaminstead ofI am fatandYou are thin. 2. As you can see in your result, it contains only 1 byte instead of two bytes (amhas two bytes), because you process up to the length of the first string. In conclusion, you just omit bytes if the first string is the shorter one and your code crashes if the second string is shorter. \$\endgroup\$stackprotector– stackprotector2020年07月26日 16:10:50 +00:00Commented Jul 26, 2020 at 16:10
C (gcc), (削除) 44 (削除ここまで) 43 bytes
x(o,r)char*o,*r;{*o|*r&&x(o+1,r+1,*o^=*r);}
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)
C (gcc), (削除) 50 (削除ここまで) 49 bytes
x(o,r)char*o,*r;{*o|*r&&x(o+!!*o,r+!!*r,*o^=*r);}
Slightly safer version (doesn't read past end of strings, requires enough memory to exist past them though - a la strcpy).
C (gcc), (削除) 61 (削除ここまで) 60 bytes
x(b,o,r)char*b,*o,*r;{*o|*r&&x(b+1,o+!!*o,r+!!*r,*b=*r^*o);}
As safe as any standard C function taking a buffer, at the cost of a few more bytes.
-1 byte from each thanks to ceilingcat!
-
\$\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– Noodle92020年07月20日 01:15:01 +00:00Commented Jul 20, 2020 at 1:15
-
1\$\begingroup\$ You're also relying on memory past the end of the shorter string to be initialised to zero. \$\endgroup\$Noodle9– Noodle92020年07月20日 01:23:11 +00:00Commented Jul 20, 2020 at 1:23
-
\$\begingroup\$ I agree it isn't the fairest, but given how strings are represented in C there isn't a much cleaner way to write this as a function. Your solution doesn't provide any means by which to access the new string, other than trapping the output stream. The only truly portable way would be to take 6 parameters: a, len(a), b, len(b), buffer, len(buffer). But defensive coding has never really been the goal of these submissions ;) \$\endgroup\$LambdaBeta– LambdaBeta2020年07月20日 20:27:53 +00:00Commented Jul 20, 2020 at 20:27
-
\$\begingroup\$ There are many ways to make your code cleaner. Not reading past the end of strings for starters. The point is that work is being done outside of your function to make it work. To change mine to return a string is trivial and certainly doesn't involve passing string lengths. Simply
malloca string buffer based on the maximumstrlens of the inputs and return that pointer after creating the result there. \$\endgroup\$Noodle9– Noodle92020年07月20日 21:38:48 +00:00Commented Jul 20, 2020 at 21:38 -
\$\begingroup\$ same can be done in mine. it also still requires work outside of the function in terms of
free. A solution would be something like this: Try it online! \$\endgroup\$LambdaBeta– LambdaBeta2020年07月20日 22:32:27 +00:00Commented Jul 20, 2020 at 22:32
Python 3.8 (pre-release), 63 bytes
lambda a,b:bytes(map(lambda c,d:c^d,a,b))+a[len(b):]+b[len(a):]
Scala 3, (削除) 30 (削除ここまで) 29 bytes
a=>b=>a zipAll(b,0,0)map(_^_)
Scala 2, (削除) 41 (削除ここまで) (削除) 39 (削除ここまで) 38 bytes
Used infix notation to turn .map into just map
a=>b=>a zipAll(b,0,0)map(a=>a._1^a._2)
Inputs and output are lists of integers
['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\$