CodeRigor

FREE LESSON

Character classification, locale & byte text

Use cctype with unsigned-byte values, trim C-locale whitespace, and separate byte-oriented case conversion from Unicode text processing.

The functions classify or convert one character value at a time. They are useful in legacy parsers and C interfaces, but their input domain is stricter than "any int" and their locale model is not a Unicode text engine.

Trim edges and keep interior bytes
Tabs and newlines are whitespace too. The internal space remains part of the field.
Trim edges and keep interior bytes
Tabs and newlines are whitespace too. The internal space remains part of the field.

Separate a stored byte from EOF

For the portable narrow-character interface, pass EOF or a value representable as unsigned char. A char read from a string can be negative on a signed-char implementation. Convert that stored char to unsigned char before calling isspace, isalpha, isdigit or tolower. The later integer promotion is safe.

fgetc has a different role: keep its int result intact while checking EOF. Converting it to char or unsigned char first can erase the sentinel distinction. After excluding EOF, you have a real byte value. Classification returns an arbitrary nonzero int for true, so test != 0 rather than == 1.

Follow one complete example

Run this program, then change one boundary while keeping the API preconditions valid. Predict its output before pressing Run.

C++
#include <cctype>
#include <cstdio>
#include <string>
int main() {
    std::string text="MiXeD";
    for(char& c:text) c=static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
    std::printf("%s %d\n",text.c_str(),std::isspace(static_cast<unsigned char>('\n'))!=0);
}
Safe character input
EOF must be checked while it is still an int sentinel.
Safe character input
EOF must be checked while it is still an int sentinel.

Declare a locale and text policy

The initial "C" locale gives this exercise a repeatable byte-text policy. The caller keeps it stable; the normalization utility does not change global locale. Calling setlocale inside a generally shared helper does not create isolated per-call configuration.

A UTF-8 character can occupy multiple bytes, and case folding can change length or require language-specific rules. Bytewise tolower does not decode that sequence or normalize it. For a machine protocol with exact ASCII grammar, use the protocol definition directly; for international text, use an appropriate Unicode text library. Do not claim either behavior from this small field adapter.

Pick a text model
This course utility intentionally handles C-locale bytes.
Pick a text model
This course utility intentionally handles C-locale bytes.

Trim a half-open range, then transform it

Use [first,last) bounds. Advance first over leading whitespace while first < last; retreat last while the same condition holds. C-locale whitespace includes tab, newline, carriage return, form feed and vertical tab in addition to space. An all-whitespace field becomes an empty range without a last-1 underflow.

Copy that remaining range once and lowercase its bytes. Interior spaces, NUL and high-bit bytes are not automatically deleted. The build tests embedded binary bytes so an accidental strlen-based conversion cannot silently shorten the field. The unsigned-char precondition is also a code-review obligation: a particular libc may tolerate negative inputs without making them portable.

Read the return value

Predict this smaller case. The check item later changes the values, so explain the rule as well as the output.

C++
#include <cctype>
#include <cstdio>
int main(){unsigned char a='A', b='\t'; std::printf("%d %c\n",std::isspace(b)!=0,std::tolower(a));}

Complete the faded example

State the missing precondition or result check before filling the code. The fill exercise supplies the rest of the implementation.

C++
while (first < last && /* classify input[first] safely */) ++first;
while (last > first && /* classify input[last-1] safely */) --last;
// Fill: convert the copied range without deleting interior bytes.

Check your model

Why is converting EOF to unsigned char different from converting a stored negative char to unsigned char?

Practice the boundary

Implement std::string normalize_field(std::string_view input) using the C character interface.

The caller keeps LC_CTYPE at the initial "C" locale for the whole call. Trim all leading/trailing characters recognized by isspace, then lowercase using tolower. Preserve every interior byte except for that case conversion. Embedded NUL is an ordinary byte here. Empty and all-whitespace inputs return an empty string. Do not mutate the input or change global locale.

Convert each char to unsigned char before passing it to cctype. This is byte-oriented C-locale normalization, not Unicode case folding or a UTF-8 validator.

Read → six checks → two bug hunts → fill → build. A successful graded submission is evidence; reading or watching alone does not grant mastery. Review cards reinforce API choices separately.

Watch and verify

The Watch & read panel links a free lecture for the relevant background and primary API references. The lecture notes say which parts are background; the tested contract here supplies the exact wrapper policy.

C standard-library course · C library review cards · Unix/Linux systems

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →