12
\$\begingroup\$

Preamble

In Stack Exchange markdown, we use ** to bold out text. For example, this markdown:

The **quick brown fox jumps over the lazy** dog.

Renders as:

The quick brown fox jumps over the lazy dog.

Of course, we use ** to close boldness as well. So less of the answer will be bold. For example:

The **quick** brown fox jumps over the **lazy** dog.

Renders as:

The quick brown fox jumps over the lazy dog.

However, if the boldness is not closed, it renders as not bold:

The **quick brown fox jumps over the lazy dog.

Renders as:

The **quick brown fox jumps over the lazy dog.

If the text has a single backslash \, the boldness does not take effect, either:

The \**quick brown fox jumps over the lazy dog.**

Renders as:

The **quick brown fox jumps over the lazy dog.**

Trailing white space results in unbolded text (note, the white space after brown is a single tab):

The** quick** brown fox jumps over the lazy dog.**

Renders as:

The** quick** brown fox jumps over the lazy dog.**

We can also use __ for bold, too, but note that only one can be active at a time. Here's a more complicated example:

The __quick**__ brown **fox__ jumps** over__ the__ lazy **dog.

Renders as:

The quick** brown fox__ jumps over__ the__ lazy **dog.

The question:

You may write a program or function, given ASCII text either as a String argument or on STDIN, where the only special characters are **, __, \ (for escaping) and trailing whitespace, determine how many bold characters there are. This value should be printed to STDOUT or returned from your function. You do not need to support very long strings; String length is guaranteed to be no more than 30K, which is the limit for a Stack Exchange post.

Fine print:

  • Can I throw an exception / other error for one case, and return normally for the other?
    • No. It must be either a clear, unambiguous, non errored return value for both cases. STDERR output will be ignored.
  • Are spaces in between words considered bold?
    • Yes. **quick brown** has 11 bold characters in it.
  • Should the \ in \**, if bold, be counted?
    • No. It renders as **, so if it should be bolded it would only be 2 characters.
  • Be completely clear: what do you mean by how many characters?
    • Total characters that would render bold. This means that ** is not rendered if it transforms text, but it is rendered if it does not.
    • Note that it's possible to make ** be bolded in several ways, e.g. **\**** -> **.
    • Don't consider the possibility that some text could be converted to italics. The only markdown rule to consider is ** = bold*.
  • On Stack Exchange, HTML Bold works too. i.e. <b></b>
    • Yes, I am aware. Don't consider this case, this is normal text.
  • What about HTML entites? e.g. &lt; -> <
    • These also should be considered as normal text, there is no HTML entity conversion.
  • I've thought of an example you didn't cover above!
    • The rules function exactly as if the text were posted on Stack Exchange, in an answer (not a comment), except that code blocks are not considered special characters. Both the four space type and the backtick type. If you are not sure about how text should be rendered, just throw it into an answer box somewhere as a test, those are the rules you should follow.

Examples:

Input:

The **quick brown fox jumps over the lazy** dog.

Output:

35

Input:

The **quick brown fox jumps over the lazy dog.

Output:

0

Input:

The __quick**__ brown **fox__ jumps** over__ the__ lazy **dog.

Output:

18

Input:

The __quick\____ brown fox **jumps over\** the** lazy \**dog.

Output:

23

Input:

The****quick brown fox****jumps over **the****lazy** dog.

Output:

11

Standard Loopholes are banned.

\$\endgroup\$
  • \$\begingroup\$ Is 18 correct for the third test case? \$\endgroup\$ – Beta Decay Sep 1 '15 at 17:32
  • \$\begingroup\$ @BetaDecay It's 7 + 11. What do you think it should be? \$\endgroup\$ – durron597 Sep 1 '15 at 17:33
  • \$\begingroup\$ I've been getting 28... I'll look over my program \$\endgroup\$ – Beta Decay Sep 1 '15 at 17:36
  • \$\begingroup\$ @BetaDecay **fox__ jumps** terminates that particular bolding. \$\endgroup\$ – durron597 Sep 1 '15 at 17:37
  • 1
    \$\begingroup\$ The question seems to suggest that \** or \__ are three-character escape sequences, but in StackExchange there are only two-character escape sequences \* or \_. So \***a** produces an asterisk followed by a bold a. There is also another escape, \\ . Should we handle that one? \$\endgroup\$ – feersum Sep 1 '15 at 17:53
5
\$\begingroup\$

rs, 107 bytes

\t/ 
(?<!\\)((\*|_){2})((?=\S)(?!\2)(\\\2|.)*?)?(?<=\S)\1/(\n)^^((^^\3))\3
\\(\*|_)/\t
[^\t\n]/
\n/_
\t_?/
(_*)/(^^\1)

Live demo and test cases.

This is a pretty crazy...thing.

The newest test case does not work yet. WIP...

Explanation

\t/ 

Replace tabs with spaces. They have the same character count, and tabs are used later on as a special character.

(?<!\\)((\*|_){2})((?=\S)(?!\2)(\\\2|.)*?)?(?<=\S)\1/(\n)^^((^^\3))\3

Replace any text of length N that should be bolded with N newlines followed by the original text.

\\(\*|_)/\t

Replace any occurrences of a delimiter immediately preceded by a slash with a tab. This is to make sure that entries like **a\*** have a character count of 2 instead of 3.

[^\t\n]/

Remove any character that's not a tab or newline.

\n/_

Replace all the newlines with underscores.

\t_?/

Remove any tabs (which represent escaped delimiters), along with any underscores that may follow them. This is related to the above issue of character counts with escaped ending delimiters.

(_*)/(^^\1)

Replace the underscore sequence with its length. This is the character count.

\$\endgroup\$
  • \$\begingroup\$ **a****b** outputs 2, it should be 6. See: a****b \$\endgroup\$ – durron597 Sep 1 '15 at 20:24
  • 1
    \$\begingroup\$ @durron597 I'm slightly confused as to how that's supposed to work. Could you add an explanation? \$\endgroup\$ – kirbyfan64sos Sep 1 '15 at 21:04
  • \$\begingroup\$ Like I said, just play around with it in an answer pane. **** is always just asteriks, which can be inside a bold or not inside a bold based on the other text. \$\endgroup\$ – durron597 Sep 1 '15 at 21:10
  • \$\begingroup\$ So, @kirbyfan64sos, how bold is the post? \$\endgroup\$ – mbomb007 Sep 1 '15 at 21:54
2
\$\begingroup\$

Python: 133 characters

import re
f=lambda s:sum(len(x[0])-4for x in re.findall(r'(([_*])\2\S.*?\2\2+)',re.sub(r'([_*])\1\1\1','xxxx',re.sub(r'\\.','x',s))))

This should work identically in both Python 2 and 3. The function f returns the number of bold characters that will be in the string it is passed when formatted by Stack Overflow's markdown system.

I think I get most of the corner cases right (including all the ones mentioned in comments so far), but its still not entirely perfect. I don't understand why x***x** doesn't render the *x in bold (like ***x** does), so my code will get at least a few inputs wrong.

The code has four main steps. The first does a regex replacement of any backslash followed by any character with an 'x' character. The second step replaces any sequence of four asterixes or underscores with four 'x' characters. The third step uses a regex findall to find all the blocks that will be italicized. The final step is a generator expression inside a sum call, that adds up the lengths of those blocks, subtracting 4 characters from each, since we don't want to include the delimiters in our count.

Here's some test output:

>>> f('The **quick brown fox jumps over the lazy** dog.')
35
>>> f('The **quick brown fox jumps over the lazy dog.')
0
>>> f('The \**quick brown fox jumps over the lazy dog.**')
0
>>> f('The** quick** brown fox jumps over the lazy dog.**')
0
>>> f('The __quick\____ brown fox **jumps over\** the** lazy \**dog.')
23
>>> f('The****quick brown fox****jumps over **the****lazy** dog.')
11
>>> f('\***a**')
1
>>> f('x***x**') # this one doesn't match the Stack Overflow input box
2
\$\endgroup\$
  • \$\begingroup\$ I have no idea why x***x** doesn't work in the input box. Bizarre \$\endgroup\$ – durron597 Sep 2 '15 at 14:59
1
\$\begingroup\$

JavaScript ES6, 91 bytes

s=>(o=0,s.replace(/\\(.)\1/g,'..').replace(/(\*\*|__)(?=\S)(.*?\S)\1/g,l=>o+=l.length-4),o)

Deals with all escapes before hand, then uses a regular expression. Lots of golfing potential.

Explanation

s=>( // Function with argument s
  o=0, // Set var "o" to 0
  s.replace( // Replace...
    /\\(.)\1/g,  // Matches \ followed by two the same characters. g means "global"
    ".." // Replace with two arbitrary characters
  ).replace( // Replace again...
     /(\*\*|__) // Match ** or __, store in "group 1"
       (?=\S)   // Make sure next character isn't whitespace
       (.*?\S)\1  // Match all characters until "group 1".
                  // Make sure last character isn't whitespace
     /g, l=> // Take the match...
       o+= // Increase o by...
         l.length // the length of the match
         - 4 // minus 4 to account for ** and __
  ), o) // Return o
\$\endgroup\$
  • \$\begingroup\$ For **a*b*c** this returns 9, which I believe is incorrect. The real count is 5 (or 3, if you take italics into account, which according to OP you should not). \$\endgroup\$ – Cristian Lupascu Sep 2 '15 at 6:39

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.