cswork / libraryOpen practice workspace

Articles

Input Parsing in Coding Assessments: Whitespace, JSON, CSV, and Malformed Data

Prepare for input parsing in coding assessments with whitespace, JSON, and CSV examples, malformed-input tests, field validation, and exact output checks.

Input parsing in a coding assessment is where a written contract becomes executable. An algorithm can be correct while the program still fails because it consumes one line too many, splits a quoted comma, accepts the wrong JSON type, or prints a diagnostic message into the expected answer.

Then test the boundary between each pair. This makes an unfamiliar input format easier to reason about and a failed example easier to diagnose.

For a practical companion exercise, open Parse CSV and format transactions. The important first step is to identify the grammar it actually specifies.

Evidence boundary: language documentation and format specifications support the technical facts below. The parsers and test cases are original preparation examples. They are not reported questions from a particular employer, and no candidate anecdote is used to predict your assessment format. Sources were checked on September 15, 2026.

First determine who owns the input boundary

A function-only task may already pass a list or object to your solution. A full-program task may require reading standard input. A file-processing exercise may provide a pathname or file handle. These interfaces require different preparation; writing a new stdin reader inside a function-only solution can consume input that the supplied harness owns.

Official platform example: HackerRank's input-format documentation explains that custom input must match the generated stub. Its array example includes a length followed by elements on separate lines. That example demonstrates why you must inspect the supplied interface; it does not define every platform's array format. HackerRank test-case input format.

Before changing starter code, identify the function signature, input constraints, return value, and output channel. Run the smallest valid example without adding your algorithm. If a harness already parses JSON, do not decode the resulting dictionary again. If the task expects a returned value, printing it is not necessarily a substitute.

Choose a parser from the grammar, not the appearance

A comma in an input example does not automatically mean CSV. A bracketed sequence is not necessarily JSON. The prompt may define a smaller custom language whose rules differ from both.

Input contractAppropriate starting point
Space or tab tokensSplit on whitespace when line boundaries carry no meaning.
Line recordsRead records without discarding meaningful blank lines or spaces.
JSON dataUse a JSON decoder, then validate the expected structure.
Quoted CSVUse a CSV reader configured for the stated dialect.

This distinction prevents destructive normalization. Trimming every line can change a name with meaningful surrounding spaces. Splitting the whole file on whitespace can destroy a sentence field. Splitting CSV on commas can turn one quoted field into several columns.

If the prompt guarantees well-formed input, implement that contract efficiently. Do not spend the assessment building a general-purpose validator unless malformed input is part of the task. If malformed input is included, establish whether to reject the entire document, report individual errors, or skip records. Those policies produce different results.

Whitespace: distinguish separators from records

Documented Python behavior: str.split() without a separator groups consecutive whitespace and discards leading and trailing whitespace. Supplying a literal separator changes the behavior: repeated separators can produce empty fields. splitlines() handles line boundaries, and a terminal newline does not create an additional trailing record. Python string methods.

For an original exercise, define this grammar: the first token is a nonnegative count written with ASCII digits; exactly that many signed integer tokens follow. Any whitespace may separate tokens, and line boundaries are irrelevant. Values use an optional sign followed by ASCII digits. Inputs are small enough to read into memory.

import re

def parse_counted_values(text):
    tokens = text.split()
    if not tokens or not re.fullmatch(r"[0-9]+", tokens[0]):
        raise ValueError("invalid count")
    count = int(tokens[0])
    if len(tokens) != count + 1:
        raise ValueError("wrong number of values")
    values = []
    for token in tokens[1:]:
        if not re.fullmatch(r"[+-]?[0-9]+", token):
            raise ValueError("invalid integer")
        values.append(int(token))
    return values

The text 3\n-2\t0 7\n represents three values: [-2, 0, 7]. The input 0 represents an empty list; an empty file is invalid because the count is missing. The input 2 9 is truncated, while 1 9 10 contains an extra value.

The lexical check is deliberate. This exercise defines a narrower token grammar than simply accepting anything a language's numeric conversion happens to understand. If your prompt allows another representation, change the grammar and its tests together.

Now change the contract to one record per line, where a record contains an ID and a free-text message. The same parser is inappropriate: it loses the message's spaces and the record boundaries. Do not reuse a helper merely because both tasks begin with text.

JSON: successful decoding is only the first check

Specification fact: JSON permits scalar values as well as arrays and objects at the document root. Its number grammar excludes NaN and infinities, and object names should be unique. A particular application can impose stricter requirements than the general format. RFC 8259.

Python implementation facts: the standard decoder raises JSONDecodeError for malformed syntax, but its defaults accept special numeric constants and retain the last value for repeated object names. Hooks let you change those behaviors. Decoding also converts JSON values into Python types; it does not enforce your application schema. Python JSON documentation.

Consider an original contract requiring exactly one object field named values, containing an array of integers. Reject duplicate keys, booleans, floats, strings, nulls, and extra fields. The strictness belongs to this exercise; it is not a claim that all JSON applications should reject the same inputs.

import json

def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError("duplicate key")
        result[key] = value
    return result

def reject_constant(value):
    raise ValueError("invalid numeric constant")

def parse_json_values(text):
    data = json.loads(
        text,
        object_pairs_hook=unique_object,
        parse_constant=reject_constant,
    )
    if not isinstance(data, dict) or set(data) != {"values"}:
        raise ValueError("expected values object")
    values = data["values"]
    if not isinstance(values, list):
        raise ValueError("expected array")
    if any(type(value) is not int for value in values):
        raise ValueError("expected integers")
    return values

The exact type check keeps a decoded boolean from being treated as an integer. Under this contract, {"values":[7]} is valid, while {"values":[true]} and {"values":[7.0]} are invalid. If the actual problem accepts integral numeric values regardless of representation, this check would be too strict. State that distinction before coding.

Do not repair malformed JSON by replacing all single quotes or deleting trailing commas. A replacement can alter string contents while concealing the real error. Likewise, evaluating input as executable code is unnecessary when a data parser exists. Keep parsing separate from any operation that executes instructions.

CSV: one logical record can span physical lines

The common CSV format described in RFC 4180 allows quoted fields containing commas and line breaks, with doubled quotes representing a quote inside a quoted field. CSV dialects vary, so confirm delimiter, quoting, header, and encoding assumptions instead of treating one example as universal. RFC 4180.

Here is an original two-record fixture. The second note spans two physical lines:

id,note
A,"red, blue"
B,"first line
second line"

A line-by-line comma split cannot preserve those records. Use a CSV reader, then apply the field rules. For this exercise, require the exact header id,note, exactly two fields per record, and a nonempty ID. Empty notes are valid, spaces remain data, and repeated IDs are allowed at the parsing layer.

import csv
import io

def parse_notes_csv(text):
    reader = csv.reader(io.StringIO(text, newline=""), strict=True)
    if next(reader, None) != ["id", "note"]:
        raise ValueError("invalid header")
    records = []
    for row in reader:
        if len(row) != 2 or row[0] == "":
            raise ValueError("invalid record")
        records.append({"id": row[0], "note": row[1]})
    return records

Documented library behavior: Python recommends opening CSV file objects with newline=''. The CSV reader handles quoting, while strict=True raises errors for bad CSV input recognized by that dialect. It is not a schema validator. DictReader can also accommodate extra or missing fields rather than rejecting them automatically. Python CSV documentation.

The fixture produces two dictionaries. The first note is red, blue; the second contains an embedded newline. A third column is invalid under the exercise contract even if the CSV syntax is readable. An unterminated quoted field is a different failure: the parser cannot complete the record.

If the prompt says to keep only the latest record for an ID, implement that after parsing. Duplicate identity is a business decision, whereas quote escaping is a format decision. Mixing them makes it difficult to explain why a record disappeared.

Classify failures before changing the parser

A malformed document, an invalid field, and a forbidden action need different fixes. A syntactically valid command to refund an unknown transaction is not a JSON parsing error. A correct transaction total printed with an extra label is not an arithmetic error.

Use this diagnostic order:

  1. Syntax: can the decoder produce a complete document or record?
  2. Fields: are required names, types, counts, and ranges valid?
  3. Rules: does the operation make sense in the current state?
  4. Output: are the values, ordering, separators, and output channel correct?

Avoid returning an empty list for every exception. That makes an invalid document indistinguishable from valid empty input. If the task requires structured errors, preserve the failure category and a useful location. If it guarantees valid input, unexpected exceptions should help you find your own mistake rather than be silently swallowed.

For all-or-nothing imports, validate the complete batch before committing changes, or use a transactional approach. Otherwise a late parse failure can leave half the records applied. For streaming tasks that explicitly allow partial progress, make the accepted-record and error policy equally explicit.

Test the contract with deliberate counterexamples

The examples above were executed locally with Python 3.9.6 using the exact article code. Current documentation is linked for behavior; this is not a claim that the examples ran inside an assessment platform.

Test zero values, missing counts, repeated whitespace, truncated input, extra tokens, JSON booleans, duplicate keys, quoted commas, embedded newlines, wrong headers, extra CSV fields, and unfinished quotes. Each case should have a stated result or error before you run it.

Also test preservation. A note containing two spaces should still contain two spaces if the contract preserves them. A parsed CSV field containing a newline should not become two records. A valid empty collection should remain different from a missing document.

For large inputs, account for buffering as well as algorithmic complexity. Reading an entire stream and building several token lists can use much more memory than the final values alone. Switch to incremental processing when the size constraints require it, while preserving the same record grammar.

Five exercises for parsing and validation practice

Practice questionWhat to check next
Parse CSV and format transactionsOutput
Validate CSV rows under multiple verification rulesField policies
Parse JSON Accounts into Typed Python Objects and Prepare FeaturesTypes and nulls
Implement a JSON ParserSyntax
Design an Extensible Python File ParserAPI design

Finish a practice attempt by tracing one input through all four layers. Then change one requirement: reject empty notes, reject duplicate IDs, or preserve blank records. The change should affect the part of the implementation that owns that rule.

Continue with Design an Extensible Python File Parser when you can explain those boundaries. A useful preparation milestone is a parser whose accepted language you can describe—and whose rejected examples you can justify.

Sources and Further Reading