52
\$\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\$ Commented Jul 10, 2015 at 21:47
  • \$\begingroup\$ @NathanMerrill Yes, and it results in A. \$\endgroup\$
    – xnor
    Commented Jul 10, 2015 at 21:54
  • 3
    \$\begingroup\$ I suspect retina would do well here. \$\endgroup\$
    – Claudiu
    Commented Jul 10, 2015 at 21:55
  • 1
    \$\begingroup\$ @Fatalize: "Backspaces will never happen on empty text." \$\endgroup\$
    – Maltysen
    Commented Jul 10, 2015 at 22:06
  • 16
    \$\begingroup\$ @Maria Tidal Tug comes back to haunt me \$\endgroup\$ Commented Jul 10, 2015 at 23:18

49 Answers 49

1
2
2
\$\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
    Commented Apr 8, 2021 at 22:14
2
\$\begingroup\$

Vim, 38 35 bytes

Saved 3 bytes thanks to Leo!

qa:%s/\([^H]\|\^\@<!.\)\^H//g
@aq@a

Try it online!

See Razetime's answer for a shorter and smarter version of this.

qa:%s/\([^H]\|\^\@<!.\)\^H//g
@aq@a

qa                                     Start recording a macro a
  :%s/                                 Substitute in entire file
      \([^H]\|\^\@<!H\)                Regex for the character to be deleted
        [^H]                           A character that isn't H
            \|                         or
                    H                  an H
              \^\@<!                   that doesn't have a ^ before it
                       \^H             All of that followed by ^H
                          //           Replace with empty string
                            g          Global flag so it replaces multiple times
                                       Enter the command
@a                                     Recursively call macro a
  q                                    Stop recording
   @a                                  Run the macro
```
\$\endgroup\$
1
  • 1
    \$\begingroup\$ You need to pass V the -v flag in order to use verbose mode that turns <cr> into a carriage return Try it online! You could also directly insert a newline in the code instead Try it online! \$\endgroup\$
    – Leo
    Commented Apr 9, 2021 at 0:45
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
    Commented Jul 11, 2015 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
    Commented Jul 11, 2015 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
    Commented Jul 13, 2015 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\$
    – ScriptKidd
    Commented Apr 3, 2020 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
    Commented Dec 25, 2015 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\$

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\$
1
\$\begingroup\$

Vyxal, 9 bytes

‛^H/ƒλ$Ṫp

Try it Online! Port of caird coinheringaahing's Jelly answer.

   /      # Split on
‛^H       # "^H"
    ƒλ--- # Reduce by
        p # Prepend to the first item
      $Ṫ  # The second with its last item removed
\$\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\$
0
\$\begingroup\$

Swift, 81 bytes

let f={(s:inout _)in while(s+"").contains("^"){s={s.replacing}()(#/.\^H/#,"",1)}}

Call it like this:

var s = "Horse^H^H^H^H^HCow"
f(&s)
print(s) // "Cow"
\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.