Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>First, using <code>std::cin &gt;&gt; name</code> will fail if the user enters <code>John Smith</code> because <code>&gt;&gt;</code> splits input on whitespace characters. You should use <code>std::getline()</code> to get the name:</p> <pre><code>std::getline(std::cin, name); </code></pre> <h1>Here we go…</h1> <p>There are a number of ways to check that a string contains only alphabetic characters. The simplest is probably <code>s.find_first_not_of(t)</code>, which returns the index of the first character in <code>s</code> that is not in <code>t</code>:</p> <pre><code>bool contains_non_alpha = name.find_first_not_of("abcdefghijklmnopqrstuvwxyz") != std::string::npos; </code></pre> <p>That rapidly becomes cumbersome, however. To also match uppercase alphabetic characters, you’d have to add 26 more characters to that string! Instead, you may want to use a combination of <code>find_if</code> from the <code>&lt;algorithm&gt;</code> header and <code>std::isalpha</code> from <code>&lt;cctype&gt;</code>:</p> <pre><code>#include &lt;algorithm&gt; #include &lt;cctype&gt; struct non_alpha { bool operator()(char c) { return !std::isalpha(c); } }; bool contains_non_alpha = std::find_if(name.begin(), name.end(), non_alpha()) != name.end(); </code></pre> <p><code>find_if</code> searches a range for a value that matches a predicate, in this case a functor <code>non_alpha</code> that returns whether its argument is a non-alphabetic character. If <code>find_if(name.begin(), name.end(), ...)</code> returns <code>name.end()</code>, then no match was found.</p> <h1>But there’s more!</h1> <p>To do this as a one-liner, you can use the adaptors from the <code>&lt;functional&gt;</code> header:</p> <pre><code>#include &lt;algorithm&gt; #include &lt;cctype&gt; #include &lt;functional&gt; bool contains_non_alpha = std::find_if(name.begin(), name.end(), std::not1(std::ptr_fun((int(*)(int))std::isalpha))) != name.end(); </code></pre> <p>The <code>std::not1</code> produces a function object that returns the logical inverse of its input; by supplying a pointer to a function with <code>std::ptr_fun(...)</code>, we can tell <code>std::not1</code> to produce the logical inverse of <code>std::isalpha</code>. The cast <code>(int(*)(int))</code> is there to select the overload of <code>std::isalpha</code> which takes an <code>int</code> (treated as a character) and returns an <code>int</code> (treated as a Boolean).</p> <p>Or, if you can use a C++11 compiler, using a lambda cleans this up a lot:</p> <pre><code>#include &lt;cctype&gt; bool contains_non_alpha = std::find_if(name.begin(), name.end(), [](char c) { return !std::isalpha(c); }) != name.end(); </code></pre> <p><code>[](char c) -&gt; bool { ... }</code> denotes a function that accepts a character and returns a <code>bool</code>. In our case we can omit the <code>-&gt; bool</code> return type because the function body consists of only a <code>return</code> statement. This works just the same as the previous examples, except that the function object can be specified much more succinctly.</p> <h1>And (almost) finally…</h1> <p>In C++11 you can also use a regular expression to perform the match:</p> <pre><code>#include &lt;regex&gt; bool contains_non_alpha = !std::regex_match(name, std::regex("^[A-Za-z]+$")); </code></pre> <h1>But of course…</h1> <p>None of these solutions addresses the issue of locale or character encoding! For a locale-independent version of <code>isalpha()</code>, you’d need to use the C++ header <code>&lt;locale&gt;</code>:</p> <pre><code>#include &lt;locale&gt; bool isalpha(char c) { std::locale locale; // Default locale. return std::use_facet&lt;std::ctype&lt;char&gt; &gt;(locale).is(std::ctype&lt;char&gt;::alpha, c); } </code></pre> <p>Ideally we would use <code>char32_t</code>, but <code>ctype</code> doesn’t seem to be able to classify it, so we’re stuck with <code>char</code>. Lucky for us we can dance around the issue of locale entirely, because you’re probably only interested in English letters. There’s a handy header-only library called <a href="http://utfcpp.sourceforge.net/" rel="noreferrer">UTF8-CPP</a> that will let us do what we need to do in a more encoding-safe way. First we define our version of <code>isalpha()</code> that uses UTF-32 code points:</p> <pre><code>bool isalpha(uint32_t c) { return (c &gt;= 0x0041 &amp;&amp; c &lt;= 0x005A) || (c &gt;= 0x0061 &amp;&amp; c &lt;= 0x007A); } </code></pre> <p>Then we can use the <code>utf8::iterator</code> adaptor to adapt the <code>basic_string::iterator</code> from octets into UTF-32 code points:</p> <pre><code>#include &lt;utf8.h&gt; bool contains_non_alpha = std::find_if(utf8::iterator(name.begin(), name.begin(), name.end()), utf8::iterator(name.end(), name.begin(), name.end()), [](uint32_t c) { return !isalpha(c); }) != name.end(); </code></pre> <p>For slightly better performance at the cost of safety, you can use <code>utf8::unchecked::iterator</code>:</p> <pre><code>#include &lt;utf8.h&gt; bool contains_non_alpha = std::find_if(utf8::unchecked::iterator(name.begin()), utf8::unchecked::iterator(name.end()), [](uint32_t c) { return !isalpha(c); }) != name.end(); </code></pre> <p>This will fail on some invalid input.</p> <p>Using UTF8-CPP in this way assumes that the host encoding is UTF-8, or a compatible encoding such as ASCII. In theory this is still an imperfect solution, but in practice it will work on the vast majority of platforms.</p> <p>I hope this answer is finally complete!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload