Summarizing the responses so far, and adding my own observations:
You should check the return value from open() and read(). If they fail, you're going to blindly continue on and print garbage.
read() returns the number of characters read, or -1 in case of error. It does NOT null-terminate its buffer, so using strlen() to find the amount of data read is wrong.
You shouldn't put a call to strlen() in the loop test condition, since you'll be re-evaluating it each iteration.
The casting of buf[i] to int in the printf statement is unnecessary. The extra arguments to variadic functions such as printf (that is, all of the arguments that make up the ...) undergo default argument promotion as follows:
chars, shorts, and their unsigned counterparts are promoted to ints
floats are promoted to doubles
Without a cast, buf[i] would get implicitly promoted to an int anyways, so adding the cast, while correct, makes the code more confusing to anyone reading it.
So, your code should look like this:
int fd = open("test.txt",O_RDONLY);
if(fd < 0)
{
fprintf(stderr, "open failed: %s\n", strerror(errno));
return;
}
char buf[128];
// It's better to use sizeof(buf) here, so we don't have to change it in case we
// change the size of buf. -1 to leave space for the null terminator
int reader = read(fd,buf,sizeof(buf)-1);
if(reader < 0)
{
fprintf(stderr, "read failed: %s\n", strerror(errno));
close(fd);
return;
}
buf[reader] = 0; // add null terminator for safety
int i;
for (i=0; i < reader; i++)
{
printf("%i: I read: %c", i, buf[i]);
}