TMOTTM
TMOTTM

Reputation: 3381

How to add a 'X' to the end of every line in C?

I'm trying to append a capital 'X' character at the end of every line of a simple text file (while switching all chars to upper case at the same time) and writing the changes to a new file.

So initially, the file faust.txt contains:

Besonders aber laßt genug geschehn!
Man kommt zu schaun, man will am liebsten sehn.
Wird vieles vor den Augen abgesponnen,

After the call, the file FAUST.txt should contain:

BESONDERS ABER LAßT GENUG GESCHEHN!X
MAN KOMMT ZU SCHAUN, MAN WILL AM LIEBSTEN SEHN.X
WIRD VIELES VOR DEN AUGEN ABGESPONNEN,X

The copying and conversion to upper case seems to work but the appending is unclear to me:

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>

int main() {

    // 1)  Open faust source file 
    int BUF_SIZE = 1;
    int faust_fh = open("faust.txt", O_RDONLY);
    char src[BUF_SIZE];

    // Destination file.
    int FAUST_fh = open("FAUST.txt", O_CREAT | O_WRONLY);

    int read_bytes = read(faust_fh, &src, BUF_SIZE);
    while (read_bytes > 0) {

        char c = src[0];
        char *C = &c; 
        src[0] = toupper(*C);

        if (src[0] == '\n') {
            char *end = &src[0];
            strcat(end, "X");
            src[0] = *end;
        }   

        write(FAUST_fh, &src, BUF_SIZE);
        read_bytes = read(faust_fh, src, BUF_SIZE);
    }   

    close(FAUST_fh);
    close(faust_fh);
    return 0;
}

Note: I should accomplish this with system calls (if possible).

Upvotes: 0

Views: 85

Answers (3)

Craig Estey
Craig Estey

Reputation: 33601

The caveats about strcat et. al. from other responders apply (e.g. you can't guarantee the buffer will have an EOS, so you can't use string functions).

Although it may be easiest to do a read with length 1, it's sometimes more efficient to have a larger buffer size.

Here's a version that works with buffer sizes > 1. I set a size of 10, but it can be whatever size you'd like:

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>

int
main(void)
{

    // 1) Open faust source file
#if 0
    int BUF_SIZE = 1;
#else
    int BUF_SIZE = 10;
#endif
    int faust_fh = open("faust.txt", O_RDONLY);
    char src[BUF_SIZE];
    char Xnewline[] = "X\n";

    // Destination file.
    // NOTE/BUG: we need to specify the mode with O_CREAT otherwise the
    // file is created with a semi-random permission mask
#if 0
    int FAUST_fh = open("FAUST.txt", O_CREAT | O_WRONLY);
#else
    int FAUST_fh = open("FAUST.txt", O_CREAT | O_WRONLY, 0644);
#endif

    char *lhs;
    size_t len;
    int read_bytes;

    while (1) {
        lhs = src;

        read_bytes = read(faust_fh, lhs, BUF_SIZE);
        if (read_bytes <= 0)
            break;

        char *end = &lhs[read_bytes];
        char *cur;

        // process/scan all bytes in the buffer
        for (cur = lhs;  cur < end;  ++cur) {
            int c = *cur;

            *cur = toupper(c);

            if (c == '\n') {
                // send all data up to, but not including the newline
                len = cur - lhs;
                if (len > 0)
                    write(FAUST_fh,lhs,len);

                // send X<newline>
                write(FAUST_fh,Xnewline,2);

                // set new left start into current buffer (just _past_ the
                // newline)
                lhs = cur + 1;
            }
        }

        // send the remainder of the current buffer [has _no_ newlines in it]
        len = cur - lhs;
        if (len > 0)
            write(FAUST_fh,lhs,len);
    }

    close(FAUST_fh);
    close(faust_fh);

    return 0;
}

Upvotes: 0

Barmar
Barmar

Reputation: 780871

You're causing undefined behavior when you call strcat(end, "X"); because end points to src, and src doesn't have a null terminator and doesn't have enough bytes allocated to concatenate anything.

If you're reading the file one byte at a time, you don't need an array. Just read into a char variable. When the character is a newline, write an X to the output file before writing the character.

Another problem: when you call open() to create the output file, you have to pass a third argument containing the desired file permissions.

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>

int main() {

    // 1)  Open faust source file 
    int BUF_SIZE = 1;
    int faust_fh = open("faust.txt", O_RDONLY);
    char src;

    // Destination file.
    int FAUST_fh = open("FAUST.txt", O_CREAT | O_WRONLY, 0644);

    int read_bytes = read(faust_fh, &src, BUF_SIZE);
    while (read(faust_fh, &src, BUF_SIZE) > 0) {
        src = toupper(src);
        if (src == '\n') {
            write(FAUST_fh, "X", 1);
        }
        write(FAUST_fh, &src, BUF_SIZE);
    }   

    close(FAUST_fh);
    close(faust_fh);
    return 0;
}

Upvotes: 2

Roberto Caboni
Roberto Caboni

Reputation: 7490

Since BUF_SIZE is 1, you are reading the input character by character.

This is fine, but this also mean that src is just a one-char array, not a nul-terminated string (an array of characters terminated by '\0' character).

And this mean all string manipulation functions, such as strcat cannot be used. All of them lead to undefined behavior when it comes to deal with araays that are not null-terminated..

In order to accomplish your task, just perform an extra write whenever you find an end of line:

if (src[0] == '\n') {
    char ecs = 'X';
    write(FAUST_fh, &ecs, 1);
    // it's always recommended to check the return value of write()
}   

All the other characters will be appended as usual. The X char write is just an insertion on the fly.

Upvotes: 1

Related Questions