Reverse words without changing capitals or punctuation

Create a program with the lowest amount of characters to reverse each word in a string while keeping the order of the words, as well as punctuation and capital letters, in their initial place.

By "Order of the words," I mean that each word is split by a empty space (" "), so contractions and such will be treated as one word. The apostrophe in contractions should stay in the same place. ("Don't" => "Tno'd").

(Punctuation means any characters that are not a-z, A-Z or whitespace*).

• Numbers were removed from this list due to the fact that you cannot have capital numbers. Numbers are now treated as punctuation.

For example, for the input:

Hello, I am a fish.


it should output:

Olleh, I ma a hsif.


Notice that O, which is the first letter in the first word, is now capital, since H was capital before in the same location.

The comma and the period are also in the same place.

More examples:

This; Is Some Text!


would output

Siht; Si Emos Txet!


Any language can be used. The program with the lowest amount of characters wins.

• How should contractions be treated? That is does Don't touch that! map to t'noD hcuot taht! or to noD't hcuot taht!? – dmckee --- ex-moderator kitten Apr 7 '13 at 4:39
• @dmckee "(Punctuation means any characters that are not a-z, A-Z, 1-9 or whitespace)" – John Dvorak Apr 7 '13 at 4:46
• @dmckee so it should map to Nod't hcuot tath! – John Dvorak Apr 7 '13 at 4:49
• Reversing each word is easy. Reversing each word and keeping capitalisation is not. – John Dvorak Apr 7 '13 at 4:53
• Yup, that's the challenge ;) just simply reversing them would be too simple and would likely come down to the language used. This is meant to make you think. – nasonfish Apr 7 '13 at 4:56

GolfScript, 5854 48 characters

" "/{.{65- 223&26<}:A,\{.A{96&\)31&@+}*}%+}%" "*


This is a GolfScript solution which became rather long. Lots of the code is actually finding out if a character is in a-zA-Z. Maybe someone can find an even shorter way of testing it.

You can try the code online. Examples:

> Hello, I am fish.
Olleh, I ma hsif.

> This; Is Some Text!
Siht; Si Emos Txet!

> Don't try this at home.
Tno'd yrt siht ta emoh.

• That online golfscript editor looks useful. Bookmarking, thanks – John Dvorak Apr 7 '13 at 11:00
• You can pull the final " " inside the % to save one. I've found other ways of testing a-zA-Z for 11 chars, but none yet for 10. – Peter Taylor Apr 10 '13 at 7:08

APL 69

Takes screen input via: t←⍞

⎕av[1↓∊(↑¨v),¨((¯1×⌽¨z)+z←¯32×~1↓¨v>97)+¨⌽¨1↓¨v←(+\v<66)⊂v←0,⎕av⍳t←⍞]

• Shouldn't APL be counted in UTF-8 bytes? :-) – John Dvorak Apr 7 '13 at 10:40
• @JanDvorak The APL+Win V5 character set is single byte. I have to convert to UTF-8 to post here in order for the characters to render correctly. ⎕av⍳t above returns an index into the character set from 0-255 for the characters in the vector t. – Graham Apr 7 '13 at 10:50

Coffeescript, 134 133 characters

alert prompt().replace /\S+/g,(x)->c=x.match r=/[a-z]/gi;return x.replace r,(y)->return c.pop()[(y<"a"?"toUpp":"toLow")+"erCase"]()


Coffeescript is (for the purposes of code golf) a slightly denser version of javascript. It doesn't have the ternary operator, but it has an escape to javascript.

Here's the javascript version:

Javascript, 152 151 characters

alert(prompt().replace(/\S+/g,function(x){c=x.match(r=/[a-z]/gi);return x.replace(r,function(y){return c.pop()[(y<"a"?"toUpp":"toLow")+"erCase"]()})}))


Indented:

alert(prompt().replace(/\S+/g,function(x){
c=x.match(r=/[a-z]/gi);
return x.replace(r, function(y){
return c.pop()[(y<"a"?"toUpp":"toLow")+"erCase"]()
})
}))


Ruby: 89 characters (including 1 for the -p switch)

Not copied Jan Dvorak's CoffeeScript solution, but after many attempts my code ended looking like an exact copy. A subconscious voice probably kept whispering “follow the white rabbit Jan Dvorak”. So upvotes for the algorithm should go to his answer.

$_.gsub!(/\S+/){|m|l=m.scan r=/[a-z]/i;m.gsub(r){|c|l.pop.send c<?a?:upcase: :downcase}}  Sample run: bash-4.2$ ruby -p reverse-word.rb <<< "Hello, I am a fish.
This; Is Some Text!
Don't touch that!
S'm00ch1e"
Olleh, I ma a hsif.
Siht; Si Emos Txet!
Tno'd hcuot taht!
E'h00cm1s


Lua, 143

print(((io.read"*l"):gsub("%w+",function(s)local r=""for i=1,#s do r=("")[s:byte(-i)>96 and"lower"or"upper"](s:sub(i,i))..r end return r end)))

• Nice try, but it should also keep punctuation in place: pastebin.com/X8QLf6fW – manatwork Apr 8 '13 at 10:08
• EDIT: oh i see now – mniip Apr 8 '13 at 19:24

EcmaScript 6 (112 characters)

The input is provided in s.

alert(s.replace(/\S+/g,x=>(_=x.match(X=/[a-z]/gi),x.replace(X,a=>_.pop()[(a<"a"?"toUpp":"toLow")+"erCase"]()))))


C# (375)

 public static string rev(string s)
{
var r = new Regex("[^A-za-z]");

var result = "";
var token = "";
foreach (var c in s)
{
if (!r.IsMatch(c + ""))
{
token += c;
}
else
{
result += new string(token.Reverse().ToArray());
result += c;
token = "";
}
}

var arr = result.ToArray();
int i = 0;
foreach (var c in s)
{
arr[i] = char.IsUpper(c) ? char.ToUpper(arr[i]) : char.ToLower(arr[i]);
i++;
}

result = new string(arr);
return result;
}


Minified

public static string rev(string s){var r=new Regex("[^A-za-z]");var result="";var token="";foreach(var c in s){if(!r.IsMatch(c+"")){token+=c;}else{result+=new string(token.Reverse().ToArray());result+=c;token="";}}var arr=result.ToArray();int i=0;foreach(var c in s){arr[i]=char.IsUpper(c)?char.ToUpper(arr[i]):char.ToLower(arr[i]);i++;}result=new string(arr);return result;}

• Shouldn't it be A-Za-z? – Cyoce Feb 27 '16 at 20:30
• @Cyoce A little detail: [A-z] is not [A-Za-z]. The first one is a common (?) mistake, 'cause it contains non-alphabetic characters. – Erik the Outgolfer Apr 26 '16 at 18:06
• Also shouldn't this be, erm, golfed? – Cyoce Apr 28 '16 at 22:01