50
\$\begingroup\$

On some terminals, pressing backspace generates the control code ^H to delete the previous character. This gave rise to a snarky idiom where edits are feigned for comedic effect:

Be nice to this fool^H^H^H^Hgentleman, he's visiting from corporate HQ.

Given a string with one or more ^H's, output the result of backspacing on each ^H. The input will use only printable characters (ASCII 32-126), and ^ will only appear as ^H. Backspaces will never happen on empty text.

You may not assume that the output environment supports control codes, in particular the backspace code \x08.

>> Horse^H^H^H^H^HCow
Cow

>> Be nice to this fool^H^H^H^Hgentleman, he's visiting from corporate HQ.
Be nice to this gentleman, he's visiting from corporate HQ.

>> 123^H45^H^H^H78^H
17

>> Digital Trauma^H^H^H^H^H^H^H^H^H^H^H^H^H^HMaria Tidal Tug^H^H^H^H^H^H^H^H^H^H^H^H^H^H^HDigital Trauma
Digital Trauma

Leaderboard

Here's a by-language leaderboard, courtesy of Martin Büttner.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/52946/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function getAnswers(){$.ajax({url:answersUrl(page++),method:"get",dataType:"jsonp",crossDomain:true,success:function(e){answers.push.apply(answers,e.items);if(e.has_more)getAnswers();else process()}})}function shouldHaveHeading(e){var t=false;var n=e.body_markdown.split("\n");try{t|=/^#/.test(e.body_markdown);t|=["-","="].indexOf(n[1][0])>-1;t&=LANGUAGE_REG.test(e.body_markdown)}catch(r){}return t}function shouldHaveScore(e){var t=false;try{t|=SIZE_REG.test(e.body_markdown.split("\n")[0])}catch(n){}return t}function getAuthorName(e){return e.owner.display_name}function process(){answers=answers.filter(shouldHaveScore).filter(shouldHaveHeading);answers.sort(function(e,t){var n=+(e.body_markdown.split("\n")[0].match(SIZE_REG)||[Infinity])[0],r=+(t.body_markdown.split("\n")[0].match(SIZE_REG)||[Infinity])[0];return n-r});var e={};var t=1;answers.forEach(function(n){var r=n.body_markdown.split("\n")[0];var i=$("#answer-template").html();var s=r.match(NUMBER_REG)[0];var o=(r.match(SIZE_REG)||[0])[0];var u=r.match(LANGUAGE_REG)[1];var a=getAuthorName(n);i=i.replace("{{PLACE}}",t++ +".").replace("{{NAME}}",a).replace("{{LANGUAGE}}",u).replace("{{SIZE}}",o).replace("{{LINK}}",n.share_link);i=$(i);$("#answers").append(i);e[u]=e[u]||{lang:u,user:a,size:o,link:n.share_link}});var n=[];for(var r in e)if(e.hasOwnProperty(r))n.push(e[r]);n.sort(function(e,t){if(e.lang>t.lang)return 1;if(e.lang<t.lang)return-1;return 0});for(var i=0;i<n.length;++i){var s=$("#language-template").html();var r=n[i];s=s.replace("{{LANGUAGE}}",r.lang).replace("{{NAME}}",r.user).replace("{{SIZE}}",r.size).replace("{{LINK}}",r.link);s=$(s);$("#languages").append(s)}}var QUESTION_ID=45497;var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe";var answers=[],page=1;getAnswers();var SIZE_REG=/\d+(?=[^\d&]*(?:&lt;(?:s&gt;[^&]*&lt;\/s&gt;|[^&]+&gt;)[^\d&]*)*$)/;var NUMBER_REG=/\d+/;var LANGUAGE_REG=/^#*\s*((?:[^,\s]|\s+[^-,\s])*)/
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src=https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js></script><link rel=stylesheet type=text/css href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"><div id=answer-list><h2>Leaderboard</h2><table class=answer-list><thead><tr><td></td><td>Author<td>Language<td>Size<tbody id=answers></table></div><div id=language-list><h2>Winners by Language</h2><table class=language-list><thead><tr><td>Language<td>User<td>Score<tbody id=languages></table></div><table style=display:none><tbody id=answer-template><tr><td>{{PLACE}}</td><td>{{NAME}}<td>{{LANGUAGE}}<td>{{SIZE}}<td><a href={{LINK}}>Link</a></table><table style=display:none><tbody id=language-template><tr><td>{{LANGUAGE}}<td>{{NAME}}<td>{{SIZE}}<td><a href={{LINK}}>Link</a></table>

\$\endgroup\$
13
  • 4
    \$\begingroup\$ Is AAA^HB^H^H valid? \$\endgroup\$ Jul 10 '15 at 21:47
  • \$\begingroup\$ @NathanMerrill Yes, and it results in A. \$\endgroup\$
    – xnor
    Jul 10 '15 at 21:54
  • 3
    \$\begingroup\$ I suspect retina would do well here. \$\endgroup\$
    – Claudiu
    Jul 10 '15 at 21:55
  • 1
    \$\begingroup\$ @Fatalize: "Backspaces will never happen on empty text." \$\endgroup\$
    – Maltysen
    Jul 10 '15 at 22:06
  • 16
    \$\begingroup\$ @Maria Tidal Tug comes back to haunt me \$\endgroup\$ Jul 10 '15 at 23:18

47 Answers 47

1
2
2
\$\begingroup\$

Japt, 8 bytes

Ue/.\^H/

Try it online!

How it works

U        // Take the input,
e/.\^H/  // and recursively replace any char followed by "^H" with
         // (nothing, defaults to an empty string).
         // Implicit: output last expression
\$\endgroup\$
1
  • \$\begingroup\$ As a small update many years later, the working solution now seems to be e".\\^H. I couldn't get the regex version to work with the current interpreter. \$\endgroup\$
    – Etheryte
    Apr 8 at 22:21
1
\$\begingroup\$

Python 2, 74 + 2 = 76 Bytes

I've tried a few approaches so far, this is the best I've been able to come up with so far.

n=input();o='';c=0
for l in n:d=l=='^';o=[o+l*(1-c),o[:-1]][d];c=d
print o
\$\endgroup\$
3
  • 4
    \$\begingroup\$ Where are those 2 extra bytes coming from? \$\endgroup\$
    – xnor
    Jul 11 '15 at 9:17
  • \$\begingroup\$ @xnor input has to be surrounded by quotes for this to work. I forgot to put that in the post. \$\endgroup\$
    – Kade
    Jul 11 '15 at 13:42
  • 1
    \$\begingroup\$ I think the usual convention has been to allow string arguments to be taken in quotes for free, but I'm not totally sure. \$\endgroup\$
    – xnor
    Jul 13 '15 at 7:19
1
\$\begingroup\$

Mumps, 84 Bytes

R Z S T="",Y=$L(Z,"^H") F I=1:1:Y{S T=T_$P(Z,"^H",I) S:I<Y T=$E(T,1,$L(T)-1)} W !,T

This could probably be made shorter as a function (1 byte I was able to save in quick testing) but I kinda like the one-liner aspect... :-)

The braces come from the Intersystems Cache flavour of Mumps which is what I'm most versed in.

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

Java - 123 bytes

I personally like the g---1 part the best.

String f(char[] a){String b="";for(int g=-1;++g<a.length;b=(a[g++]=='^'?b.substring(0,b.length()-1):b+a[g---1]));return b;}

expanded (slightly):

  String f(char[] a) {
      String b = "";
      for (int g = -1;
           ++g < a.length;
           b = (a[g++]=='^' 
                ? b.substring(0, b.length() - 1) 
                : b + a[g---1])
      );
      return b;
  }
\$\endgroup\$
1
\$\begingroup\$

Batch - 138 bytes

@!! 2>nul||cmd/q/v/c%0 %1&&exit/b
set s=%1&for /F %%a in ('"prompt $H&echo on&for %%b in (1)do rem"')do set D=%%a
echo %s:^H=!D! !D!%

The first line is a way of saving a few bytes over the lengthy @echo off&setLocal enableDelayedExpansion (which turns echo off and enables the delayed expansion of variables, in case you were wondering). I explained it in Tips for Golfing in Batch.

The second line is a neat little trick to save the a backspace control character into a variable. It's pretty hacky, and I can't pretend to take credit for it. It's sort of explained here. Basically uses the prompt command to generate a backspace character and captures it in a variable - in this case !D!.

The final line then performs the simple string manipulation of - replace ^H with !D!<SPACE>!D!.

C:\>bsp.bat "testing^H^H^H test"
"test test"

Unfortunately it breaks with cases like "AAA^HB^H^H" - where it should produce "A", it instead produces "A"B. Which is somewhat confusing. I'll have to look into how Batch string manipulation works in some more depth.

C:\>bsp.bat "AAA^HB^H^H"
"A"B

Thanks to to some helpful people over here - I now realize that I was only saving the backspace character (0x08), and so was only overwriting the characters. It now works with examples like the following:

C:\>bsp.bat "AAA^HB^H^H"
"A"
\$\endgroup\$
1
  • \$\begingroup\$ there's a shorter version: set s=%1&for /F %%a in ('prompt $H^&cmd /k^<nul')do set D=%%a \$\endgroup\$ Apr 3 '20 at 5:31
1
\$\begingroup\$

JavaScript, 60 bytes

b=n=>(v=n.search('^H'))>0?b(n.replace(n.slice(v-1,v+2),'')):n

Not as good as edc65's answer, but I wanted to try something that didn't use Regex.

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

bash, 50 bytes

while [ "$a" != "${a/?^H/}" ];do a=${a/?^H/};done

Sample:

a=$'Horse^H^H^H^H^HCow'
while [ "$a" != "${a/?^H/}" ];do a=${a/?^H/};done
echo $a
Cow

a="Be nice to this fool^H^H^H^Hgentleman, he's visiting from corporate HQ."
while [ "$a" != "${a/?^H/}" ];do a=${a/?^H/};done
echo $a
Be nice to this gentleman, he's visiting from corporate HQ.

This could work with real (binaries) backspaces as well:

a=$'Be nice to this fool\b\b\b\bgentleman, he'\'$'s visiting from corporate HQ.'
echo $a.. but:
Be nice to this gentleman, he's visiting from corporate HQ... but:
printf %q\\n "$a"
$'Be nice to this fool\b\b\b\bgentleman, he\'s visiting from corporate HQ.'

while [ "$a" != "${a/?$'\b'/}" ];do a=${a/?$'\b'/};done
printf %q\\n "$a"
Be\ nice\ to\ this\ gentleman\,\ he\'s\ visiting\ from\ corporate\ HQ.
\$\endgroup\$
1
\$\begingroup\$

><>, 39 38 36 bytes

There was no ><> answer so I thought I'd add one.

i:0(?v:'^'=?\
     ~   ~~i/
;!?lr<ro
\$\endgroup\$
1
\$\begingroup\$

JavaScript, 60 Bytes

I know there is already a JS answer here, but wanted to do it without regexes, because regex is really almost a language by itself. I'm sorry if you feel I shouldn't post this, this is my first post.

X is the string to be operated upon.

while((z=x.indexOf('^D'))>0){x=x.slice(0,z-1)+x.slice(z+2);}

Test

Put this into your browser's address bar.

javascript:x=prompt('Enter the sentence to be erased');while((z=x.indexOf('^D'))>0){x=x.slice(0,z-1)+x.slice(z+2);}alert(x);

I love JS because it will let you do things like saying (z=x.indexOf('^D))>0, and it will both assign z.indexOf('^D'); to x and evaluate z.indexOf('^D') in the condition, which saves me precious bytes in this problem. It's not very useful otherwise, but it's fun!

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

Emacs Lisp, 45 73 bytes

(lambda(s)(while(string-match".^H"s)(set's(replace-match"" nil nil s)))s)

Searches for the first occurence of anything else and ^H as long as it exists and replaces it with an empty string.

Old, incorrect version

(lambda(s)(replace-regexp-in-string".^H"""s))
\$\endgroup\$
1
  • \$\begingroup\$ Sorry, but I'm afraid this is not enough – you have to do the replacing in multiple steps so “one^Hff^H^H^Huch” gets transformed into “ouch”. \$\endgroup\$
    – manatwork
    Dec 25 '15 at 17:03
1
\$\begingroup\$

jq 1.5, 41 37 bytes

(34 characters code + 3 characters command line option.)

reduce(./"^H")[]as$t("";.[:-1]+$t)

Sample run:

bash-4.3$ bin/jq -R -r 'reduce(./"^H")[]as$t("";.[:-1]+$t)' <<< "Be nice to this fool^H^H^H^Hgentleman, he's visiting from corporate HQ."
Be nice to this gentleman, he's visiting from corporate HQ.

On-line test (Passing -R through URL is not supported – so input passed as JSON string literal. Passing -r through URL is not supported – check Raw Output yourself.)

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

Oracle SQL 11.2, 172 bytes

WITH v(s,i)AS(SELECT:1,INSTR(:1,'^')FROM DUAL UNION ALL SELECT LPAD(s,i-2)||SUBSTR(s,i+2),INSTR(s,'^',1,2)-3 FROM v WHERE i>0AND'^'<>s)SELECT s FROM v WHERE INSTR(s,'^')=0;

Un-golfed

WITH v(s,i) AS                        -- Recursive view, s-> string, i->pos of first ^
(
  SELECT :1,INSTR(:1,'^')             -- Initialisation view 
  FROM   DUAL
  UNION ALL
  SELECT LPAD(s,i-2)||SUBSTR(s,i+2),  -- Remove the ^ at pos i and the characters before and after 
         INSTR(s,'^',1,2)-3           -- Compute the pos of the next ^ (the 2nd of s as before the remove just above)
  FROM   v 
  WHERE  i>0                          -- Exit clause : no more ^
    AND  s<>'^'                       -- Needed to circumvent oracle's cycle detection, without it 123^H45^H^H^H78^H will fail
)
SELECT s FROM v WHERE INSTR(s,'^')=0; -- Keep only the row without any ^
\$\endgroup\$
1
\$\begingroup\$

Jelly, 9 bytes

œṣ⁾^HṖ;¥/

Try it online!

Not a new approach, but the question was missing a Jelly answer

How it works

œṣ⁾^HṖ;¥/ - Main link. Takes a string S on the left
  ⁾^H     - Yield "^H"
œṣ        - Split on "^H"
       ¥/ - Reduce by the following:
     Ṗ    -   Remove the last character
      ;   -   And append the right argument
\$\endgroup\$
1
  • \$\begingroup\$ ping me in an hour :P \$\endgroup\$
    – Wezl
    Apr 8 at 22:14
1
\$\begingroup\$

K (ngn/k), 23 16 bytes

-7 bytes by rereading question (specifically, "^ will only appear as ^H")

{x_/3#-1+x?"^"}/

Try it online!

Removes one instance of ?^H on each invocation, and is run until convergence (i.e. no more ^'s are present).

  • {...}/ run function on (implicit) input until it converges
  • -1+x?"^" get the index of the character immediately prior to the first ^ in x
  • x_/3# drop the value occurring at that index three times
\$\endgroup\$
0
\$\begingroup\$

Javascript ES6, 41 39 bytes

f=s=>s==(s=s.replace(/.\^H/,""))?s:f(s)
\$\endgroup\$
0
\$\begingroup\$

𝔼𝕊𝕄𝕚𝕟, 8 chars / 10 bytes (noncompetitive)

ïė/.\^H/

Try it here (Firefox only).

Works like all of the other JSGL's.

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

APL (Dyalog Unicode), 18 bytes

'.\^H'⎕R''⍠'ML'1⍣≡

Try it online!

\$\endgroup\$
1
2

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.