# Alphanumeric balance

Write a program which gets a string as its input, and counts the number of alphanumeric and non-alphanumeric characters in it. It has to display its result like this:

input: http://stackexchange.com
output: 20 + 4 = 24

The catch is, your source code has to have the same number of alphanumeric characters as non-alphanumeric ones. Comments are not allowed, whitespace is ignored. (The language called Whitespace might compete for votes, but will not be selected as winner, obviously)

Characters in the code must have at least some minor justification, they should not be completely superfluous. For example, longer variable names are allowed, i = (j*3)+4; instead of i = j*3+4; is also allowed. However, i = i + 1;;;;;; is not.

Besides this, standard code-golf rules apply.

• If I define a new, preprocessed variant of Ook where the keywords are O., O?, and O! and then any program I write meets the character class restriction... Of course it is likely to lose on the length business. – dmckee Mar 14 '14 at 17:58
• will it all be ascii? – Jordon Biondo Mar 14 '14 at 17:58
• @JordonBiondo : I was thinking of anything you want from the full 8 bit ANSI to unicode, but if your code only supports 7 bit ASCII, I will accept it as well. – vsz Mar 14 '14 at 18:19
• Is the whitespace inside the output-string counted into the non-alphanumerics? Or ignored with all the other (non-string-literal) whitespace? – Kninnug Mar 14 '14 at 22:25
• @dmckee: If you're going to define your own language, just define a variant of the language of your choice where non-empty programs work just like in the base language, but the empty program is preprocessed into code that does exactly what the question asks for. – user2357112 Mar 15 '14 at 0:56

# Perl, 32 + 32 = 64

The string is expected in STDIN. The output is written to STDOUT. White space is ignored. My interpretation of the task is that the program should be able to run on itself to get the score.

$/ =$,;
$_ = <>; s x\sxxg;$\ = length;
print s x[0-9a-z]xxgi,
' + ',
s x.xxg,
' = '


$/ =$,; # The input separator becomes undefined, because the default for $, is "undef"$_ = <>; # now $_ takes the whole file (STDIN) instead of the first line s x\sxxg; #$_ =~ s/\s//g;
# white space is removed from $_$\ = length; # The number of the other characters are put into $\, # which is automatically printed the end of "print". print s x[0-9a-z]xxgi, # s/[0-9a-z]//gi # Remove alphanumeric characters and return their count ' + ', s x.xxg, # s/.//g # Remove the remaining special characters and return their count. # "." does not catch new lines, but we have already # removed white spaces including new lines. ' = '  I found several variations with the same byte counts, e.g.: $/ = $x;$_ = <>, s x\sxxg;
$\ = split$x;
print s x[\da-z]xxgi,
" + ",
s x.xxg,
' = '


## Examples

• Example from the question:

echo 'http://stackexchange.com' | perl a.pl
20 + 4 = 24

• Running on itself (a.pl):

cat a.pl | perl a.pl
32 + 32 = 64


The file size is 104 bytes, thus 40 bytes are ignored as white space.

# Perl, 29 + 29 = 58

$_=<>;s x\sxxg;$\=length;print s x[0-9a-z]xxgi,' + ',s/.//g,' = '


The string is expected at STDIN and it is limited to the first line. The result is printed to STDOUT. White space is ignored.

$_ = <>; s x\sxxg; # same as s/\s//gx; removes white space;$\ = length(_); # sum is automatically appended at the end of print print sx[0-9a-z]xxgi, # same as s/[0-9a-z]//gi; # the number of alphanumeric characters ' + ', s/.//g, # the number of the remaining special characters ' = '  ## Examples File a.pl contains the Perl script. • Example from the question: echo 'http://stackexchange.com' | perl a.pl 20 + 4 = 24  • Running on itself: cat a.pl | perl a.pl 29 + 29 = 58  The file size of a.pl is 65 bytes, thus 7 bytes are ignored as white space. • It seems you are assuming the input is on only one line ... I didn't see anything about that in the spec? Also, what is the justification for the /x flag in the first replace? – skibrianski Mar 15 '14 at 0:51 • @skibrianski: (a) The question is not too clear about the specification of "string". Now I have added a variant that can read whole files. (b) Also it is not clear to me, how white space should be treated by the script. My interpretation is that white space is ignored in both the task and in the score. (c) The /x flag allows white space in the pattern to increase the readability. The updated answer makes use of it. – Heiko Oberdiek Mar 15 '14 at 1:35 • Re a), the author doesn't say anything about what will be in the string, so I'd guess it's unwise to make assumptions, which to me means newlines must be allowed. Re b) agreed, it's not clear. Re c) Right, but in your answer whitespace doesn't add readability to my eye, it just adds a alphanumeric character... Maybe I'm being too hard on this point, but it's revealing to me that you only use /x in one of your regexes, presumably to add that one last extra alphanumeric to get the counts to align =) Still I like your answer. I cooked up something pretty similar. – skibrianski Mar 15 '14 at 2:07 • haha now we have essentially identical code =) good show =) – skibrianski Mar 15 '14 at 2:49 • @skibrianski: :-) Thanks, you give me a reason to post one of the other variants with a little more differences. However the byte count remains. – Heiko Oberdiek Mar 15 '14 at 3:09 # C - 96 (48+48) characters It's somewhat readable. There's room for improvement, though. i,j;main(_){while((_=getchar())>=0)isspace(_)||(isalnum(_)?i++:j++);printf("%i + %i = %i",i,j ,i+j);}  # Bash + coreutils, 72 (36 + 36) non-whitespace chars a=tr -dc [:alnum:]<<<1|wc -c
n=tr -dt [:space:]<<<$1|wc -c echo$a + $[n-a] =$n


Output:

$./alnumbalance.sh http://stackexchange.com 20 + 4 = 24$ ./alnumbalance.sh "$(cat alnumbalance.sh)" 36 + 36 = 72$


### Pure Bash, 92 (46 + 46) non-whitespace chars

nosp=${1//[[:space:]]} noaln=${nosp//[[:alnum:]]}
echo $[${#nosp}-${#noaln}] +${#noaln} = ${#nosp}  Output: $ ./alnumbalance.sh http://stackexchange.com
20 + 4 = 24
$./alnumbalance.sh "$(cat alnumbalance.sh)"
46 + 46 = 92
$ • Woohoo - it even beats golfscript! ;-) – Digital Trauma Mar 14 '14 at 20:49 • What about control characters? [:alnum:] is not the inverse of [:punct:]. Try eg head -c256 /dev/urandom | tr -d [:alnum:][:punct:] – skibrianski Mar 15 '14 at 1:55 • @skibrianski good point. I've edited the answer to take this into account. – Digital Trauma Mar 15 '14 at 2:07 # PowerShell (43+43=86) Golfed function alf($i){$a=0;$n=0;[char[]]$i|%{if($_-match"[a-zA-Z0-9]"){$a++}else{$n++}}; write-host "$a+$n=$($a+$n)"}  Un-golfed function alf($i){
$a=0;$n=0;
[char[]] $i | %{ if ($_ -match "[a-zA-Z0-9]") { $a++ } else {$n++ } };
write-host "$a+$n=$($a + $n)" }  Test PS > alf "http://stackexchange.com" 20+4=24  Testing with the code itself to pass the criteria PS > alf "function alf($i){$a=0;$n=0;[char[]]$i|%{if($_-match"[a-zA-Z0-9]"){$a++}else{$n++}}; write-host "$a+$n=$($a+$n)"}" 43+43=86  " has been escaped with  which is not part of string. ### GolfScript, 74 characters (=37+37) {+}:PLUS;.,.@10,''*26,{65PLUS.32|}%PLUS$-,\1$-' + 'PLUS\PLUS' = 'PLUS\PLUS  Online test for the code with the code as input. # Ruby 38+38=76 This program counts trailing newline in the input. puts"#{a=gets.scan(/[a-z0-9]/i).length}+#{b=$_.scan(/\W|_/).length}=#{a+b}"


The character count is done by the program itself: $ruby alphabalance.rb alphabalance.rb :) # Powershell, 70 bytes (=35+35) param($s)"$(($l=$s.Length)-($n=($s|sls '\W' -a).Matches.Count))+$n=$l"  Test script: $f = {
param($s)"$(($l=$s.Length)-($n=($s|sls '\W' -a).Matches.Count))+$n=$l"
}

&$f "http://stackexchange.com" &$f $f.toString().Trim()  Output: 20+4=24 35+35=70  # Powershell, 70 bytes (=35+35), alternative "$(($l="$args"|% Length)-($n=($args|sls '\W'-a).Matches.Count))+$n=$l"


# Python 2 (60+60 = 120)

Tough one, there is probably room for improvement. Like the fact the function itself can be used to evalulate its own alphnumeric balance.

def f(s):
i=j=0
for c in s:
t=ord(c)
if (t!=2**5):
i+=1
if (48<=t<=57 or 65<=t<=90 or 97<=t<=122):
j+=1
print j,'+',i-j,'=',i


Test:

>>> f("http://stackexchange.com")
20 + 4 = 24

• What version of Python is this? – Gigaflop Dec 11 '18 at 14:55
• @Gigaflop I edited it. The print statement is Python 2 only, as is the backtick syntax for repr. – mbomb007 Dec 11 '18 at 20:07

# C++, 146 (73+73) 178 (89+89) non-whitespace characters#

Original included <algorithm> for no good reason. Oops.

//create a test string
#include<string>
std::string a = "\?\?=include <cstdio>\
int x,y;\
int main()\?\?<\
for(char c : a)\
!isspace(c) ? (isalnum(c) ? y++ : x++) : 0;\
printf(\"%d\?\?/t%c\?\?/t%d\?\?/t%c\?\?/t%d\?\?/n\",y,'+',x,'=',(x+y));\
\?\?>";

//Code itself starts here
??=include <cstdio>
int x,y;
int main()??<
for(char c : a)
!isspace(c) ? (isalnum(c) ? y++ : x++) : 0;
printf("%d??/t%c??/t%d??/t%c??/t%d??/n",y,'+',x,'=',(x+y));
??>


I am only counting characters in the lines after //Code itself starts here. In particular, this means not counting #include <string>. I am also counting trigraphs as three characters each, which is perhaps debatable. Note that in testing the program on its own source code some care is required to prevent trigraph replacement within the string literal.

There are some peculiar design decisions here -- in most production code you will not encounter trigraphs and range-based-for loops in the same function -- but I think all within the bounds of 'justifiable'.

# python 52 +52 = 104

Interesting challnge because python avoids non-alphanumeric characters.

def f(_):
_=_.replace(" ","");l=len(_);a=sum([c.isalnum() for c in _][:l]);print("{0} + {1} = {2}".format(a,l-a,l))


Minor justification for using slice: it speeds it up (maybe?)

• Try using Python 2, since print doesn't require parenthesis, and using the '%d + %d = %d' % (a,l-a,l) method. That should save some characters. – mbomb007 Jan 14 '15 at 16:52

# Julia, 64

f(s)=(b=endof(s);a=sum([isalnum(c) for c in s]);"$(a) +$(b-a) = $(b)";)  All the only unnecessary non alphanumeric characters are the last ; and some of the () in the string formatting., it came out almost perfectly balanced, and as a power of 2 without much fiddling. julia> f("http://stackexchange.com") "20 + 4 = 24" julia> nowhite(s)=join(split("s"," ")) julia> f(nowhite("f(s)=(b=endof(s);a=sum([isalnum(c) for c in s]);\"$(a)+$(b-a)=\$(b)\";)"))
"32 + 32 = 64"


# perl, 64 non-whitespace chars:

$/=$,;
$_=<>; s 0\s00g;$\=length;
print s 1[a-z0-9]11ig .
" + " .
s 2.22g .
" = "


Clarified slightly via perl -MO=Deparse and some comments:

$/ =$,;               # input record separator = a variable defaulting to undef
$_ = <ARGV>; # slurp stdin s/\s//g; # strip whitespace$\ = length $_; # output record separator = total length of string sans whitespace print s/[a-z0-9]//gi . ' + ' . s/.//g . ' = '; # count alphanumerics, then everything else  The ORS,$\ is appended automatically in every call to print, putting the total count at the end.

• Had 66 characters on my first pass. Thanks to Heiko Oberdiek for showing that you can unset $/ with fewer chars by setting it to$, =) – skibrianski Mar 15 '14 at 2:46

## Python 2, 50 + 50 = 100

import re
def f(i):
w = re.sub('\s', '', i)
s = re.subn('[\W_]', '', w)
a = len(s[0])
print '%d + %d = %d' % (a, s[1], a+s[1])


Run it here: http://repl.it/8CH

# Rebol (64 + 64 = 128)

f: func [x] [
c: :charset
a: c [#"a" - #"z"]
s: c [#" " #"^/" #"^-"]
n: complement union a s
a+: n+: 0
parse x [
some [
a (++ a+) |
n (++ n+) |
s
]
]
print [a+ "+" n+ "=" a+ + n+]
]


### Usage example (in Rebol console):

>> f "http://stackexchange.com"
20 + 4 = 24


NB. Program ignores spaces, tabs & newlines from counts.

# J - 46 + 46 = 92

Counts whitespace, so you can't self-test without a modification. Takes input on stdin. Has a bad mouth, should go wash it out with soap.

;":&.>(+/;' + ';(#-+/);' = ';#)(e.~1!:1@1:)(,toupper)'golfscriptSUCKSabdehjkmnquvwxyz',,":"0 i.10


Usage:

   ;":&.>(+/;' + ';(#-+/);' = ';#)(e.~1!:1@1:)(,toupper)'golfscriptSUCKSabdehjkmnquvwxyz',,":"0 i.10
http://stackexchange.com
20 + 4 = 24

NB. modification for self-test:    vvvvvv - remove spaces, the only whitespace
;":&.>(+/;' + ';(#-+/);' = ';#)(e.~' '-.~1!:1@1:)(,toupper)'golfscriptSUCKSabdehjkmnquvwxyz',,":"0 i.10
;":&.>(+/;' + ';(#-+/);' = ';#)(e.~1!:1@1:)(,toupper)'golfscriptSUCKSabdehjkmnquvwxyz',,":"0 i.10
46 + 46 = 92


# Javascript - 76 (38 + 38)

_ = prompt()
o = _.match(/[a-z0-9]/gi).length
$= _.length - o alert(o + " + " +$ + " = " + (o + $))  Sample Input: http://stackexchange.com Output: 20 + 4 = 24 Running on self: var a = '_ = prompt()o = _.match(/[a-z0-9]/gi).length$ = _.length - oalert(o + " + " + $+ " = " + (o +$))'

var letters = a.match(/[a-z0-9]/g).length;
var nons = a.match(/[^a-z0-9 ]/g).length; // excludes whitespace from count

console.log(nons + " = " + letters); // 38 = 38 :)


P.S. For those concerned with (o + \$) being done to maintain alphanumeric balance, it is not so. Because after seeing o + " + " JS would decide all + to be string concatenaters rather than number adders. Thus the parentheses are necessary, or 20 + 4 would become 204 rather than 24 :D

Happy Coding!

# Clojure: (31 + 31 = 62) non-whitespace chars

(def ff #(let [c count y (c %) x (c (re-seq #"\w" %))] (str x " + " (- y x) " = " y)))


Output:

alphabalance.core=> (ff "http://stackexchange.com")
"20 + 4 = 24"


# CJam, 27 + 27 = 54

CJam is a couple of months newer than this challenge, so this answer is not eligible for the green checkmark. It was a fun exercise anyway, though!

ea0=eu{A,s'[,65>+#)g}%_:+1@f-:+ea0=,]"DODDaD"36f-3/]zo


It takes the input string as a command-line argument, so it won't work in the online interpreter, but you can test it with the Java interpreter.

## Explanation

"Distinguish alphanumeric characters:";
ea0=eu{A,s'[,65>+#)g}%
ea0=                   "Get the first command-line argument.";
eu                 "Convert it to upper case.";
{             }% "Map this block onto each character.";
A,s             "Get the string '0123456789'.";
'[,          "Get a string with all characters from the null byte to Z.";
65>       "Remove the first 65 characters, to leave A to Z.";
#     "Find character in that string. Returns -1 if not alphanumeric.":
)g   "Increment and get signum. Yields 1 for alphanumeric characters,
0 otherwise.";

"Now we've got an array of 0s and 1s. Let's do the counting:";
_:+1@f-:+ea0=,]
_               "Duplicate array.";
:+             "Get the sum. This is the number of alphanumeric characters.";
1@           "Push a 1 and pull up the other copy of the array.";
f-         "Subtract each element from 1, this swaps 0s and 1s.";
:+       "Get the sum. This is the number of symbol characters.";
ea0=   "Get the first command-line argument again.";
,  "Get its length. This is the total number of characters.";
] "Collect everything in an array.";

"And now the formatting:";
`