NivaarExam PrepOfficial exam papers ↗

25-Comp-A4 Program Design and Data Structures · December 2014

Question 7 of 9: File I/O — Testing Two Files for High Similarity

Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)

Notes on this paper

Paper format. 98-Comp-A4 Program Design and Data Structures, December 2014 — 3 hours, closed book, no calculator permitted. Nine questions, each of equal weight (20 marks); candidates answer any six, so a complete paper is 120 marks. Pseudocode or any high-level language is accepted, and the examiner's note states explicitly that marking emphasises the operation of the program, not syntactic details. All nine questions are answered below, because the whole set is the more useful revision resource. Answers are given in C or C++ as the question dictates; each is compilable as written, but a clear, correctly reasoned pseudocode answer would earn the same marks.

Reference texts for this subject.

  • Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms, 4th ed. — tree traversals (ch. 12), partitioning (ch. 7), asymptotic analysis (ch. 3).
  • Weiss, Data Structures and Algorithm Analysis in C, 2nd ed. — linked lists (ch. 3), binary search trees (ch. 4).
  • Deitel & Deitel, C++ How to Program, 10th ed. — class design and operator overloading (ch. 9–10), file streams (ch. 14).
  • Kernighan & Ritchie, The C Programming Language, 2nd ed. — character I/O idioms (ch. 1, 7), pointers and structures (ch. 5–6).
  • Stroustrup, The C++ Programming Language, 4th ed. — value semantics and const-correctness (ch. 16–18).

The Computer Engineering citation list is built around architecture and networking texts (Patterson & Hennessy, Tanenbaum, Mano); this subject is programming and data structures, so the works above are cited instead.

Question 7: File I/O — Testing Two Files for High Similarity (20 marks)

Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.

Given. Two file names supplied on standard input; a definition of similarity that ignores blanks, tabs and newlines entirely; an explicit warning that the files may differ in size; and a requirement to answer false at the earliest possible moment.

Find. A program that prints true when the two files agree character for character after all white space is removed, and false otherwise, stopping at the first disagreement.

Approach. Read both files as streams of significant characters, skipping white space in a helper, and compare one significant character from each per iteration; any mismatch — including a character against end of file — ends the comparison immediately.

  1. Restate the requirement as an equality of filtered streams. Two files are highly similar exactly when the sequences obtained by deleting every white-space character are equal: $$\boxed{\sigma(F_{1}) = \sigma(F_{2}), \quad \sigma(F) = \text{the characters of } F \text{ with white space removed}}$$ Framing it this way makes the size warning in the question a non-issue: file lengths are irrelevant, only the filtered sequences matter.
  2. Do not read either file into memory. The obvious implementation — load both files, strip white space, compare the two strings — needs space proportional to the file sizes and cannot possibly stop early, because it has already read everything. Comparing as streams needs only two characters at a time, giving $O(1)$ space and, in the failing case, time proportional only to the matching prefix.
  3. Put the white-space skipping in one helper. A function that returns the next non-white-space character, or EOF, is the whole of the filtering logic, and having exactly one of them guarantees both files are filtered identically. isspace from <ctype.h> already covers blanks, tabs and newlines, and also carriage returns and form feeds, which is the behaviour wanted when files come from different operating systems.
  4. Let a single comparison handle unequal lengths. If one file runs out of significant characters before the other, the exhausted stream yields EOF while the other yields a real character; since EOF is guaranteed distinct from every character value, the test a != b catches that case with no extra code. The files agree only if both reach EOF on the same iteration.
    #include <stdio.h>
    #include <ctype.h>
    
    /* Return the next character of f that is not white space, or EOF. */
    static int next_significant(FILE *f)
    {
        int c;
        while ((c = fgetc(f)) != EOF && isspace(c))
            ;                       /* blanks, tabs and newlines are skipped */
        return c;
    }
    
    int main(void)
    {
        char name1[FILENAME_MAX], name2[FILENAME_MAX];
        FILE *f1, *f2;
        int similar = 1;
    
        /* The two file names are read from standard input. */
        if (scanf("%s %s", name1, name2) != 2) {
            fprintf(stderr, "expected two file names\n");
            return 2;
        }
    
        f1 = fopen(name1, "r");
        f2 = fopen(name2, "r");
        if (f1 == NULL || f2 == NULL) {
            perror("fopen");
            if (f1) fclose(f1);
            if (f2) fclose(f2);
            return 2;
        }
    
        for (;;) {
            int a = next_significant(f1);
            int b = next_significant(f2);
    
            if (a != b) {           /* first difference: stop at once */
                similar = 0;
                break;
            }
            if (a == EOF)           /* both streams ended together */
                break;
        }
    
        fclose(f1);
        fclose(f2);
    
        printf(similar ? "true\n" : "false\n");
        return similar ? 0 : 1;
    }
  5. Confirm the early exit and the boundary cases. The loop breaks on the first differing pair, so a mismatch in the opening bytes of two enormous files costs two reads. Worked cases: a file containing “hello world” and one containing “hello” then a newline then “world” are highly similar, because both filter to helloworld; “hello world” against “hello world!” is not, and is rejected when one stream yields ! and the other EOF; and an empty file is highly similar to a file of nothing but white space, since both filter to the empty sequence. Overall cost is $O(n)$ time and $O(1)$ space.
Question 7 — results
CaseFile 1File 2Output
Same text, different line breakshello worldhello / worldtrue
Same text, different spacing and tabs  a b  c  abctrue
Extra punctuationhello worldhello  world!false
Empty against all white space(empty)spaces and newlines onlytrue
One a prefix of the otherabcabcdfalse
Time / space$O(n)$ time, stopping at the first mismatch$O(1)$ space