What's wrong with my solution to K&R exercise 1-22? - c

Exercise 1-22 of The C Programming Language is as follow:
Write a program to "fold" long input lines into two or more shorter
lines after the last non-blank character that occurs before the n-th
column of input. Make sure your program does something intelligent
with very long lines, and if there are no blanks or tabs before the
specified column.
This is the code:
#include <ctype.h>
#include <stdio.h>
#define MAXLINE 500
#define FOLD_LENGTH 15
/* _getline: read a line into s, return length */
size_t _getline(char s[], int lim)
{
int c;
size_t i;
for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
int main()
{
int c;
char line[MAXLINE];
char temp;
unsigned last_space_idx = 0, i, offset = 0;
while (_getline(line, MAXLINE) != 0) {
for (i = 0; line[offset+i] != '\0'; i++) {
if (i == FOLD_LENGTH) {
temp = line[offset+last_space_idx];
line[offset+last_space_idx] = '\0';
printf("%s\n", line+offset);
line[offset+last_space_idx] = temp;
offset = last_space_idx;
i = 0;
continue;
}
if (isspace(line[offset+i])) {
last_space_idx = offset+i;
}
}
printf("%s\n", line+offset);
}
return 0;
}
This is the sample input I'm using:
Penny Lane is in my ears and in my eyes
There beneath
the blue suburban skies
And this is the output I get:
Penny Lane is
in my ears and in my ey
and in my eyes
eyes
eyes
eyes
What's the bug here? I really have no clue.

Lots of errors. You do this:
last_space_idx = offset+i;
But you also do this:
temp = line[offset+last_space_idx];
Which means that temp = line[(2 * offset) + last_observed_space_relative_to_offset].
You also do this:
offset = last_space_idx;
That means the offset becomes equal to the last observed space, so you'll have a preceding space on every line after the first, like this:
Penny lane is
in my ears
and in my eyes
Your _getline() method does this:
if (c == '\n') {
s[i] = c;
++i;
}
That means any line returns are preserved, so if you have There beneath\nthe blue suburban skies as the input you'll get this output:
There beneath
the blue suburban skies
Lastly, each new line you read uses the last space index and offset from the previous line. You need to reset them before the for loop starts.
Here's a fixed version. I've tidied up the style a little and replaced the printf() bodge with a string format that will print a substring.
#include <stdio.h>
#include <ctype.h>
#include <stdio.h>
#define MAXLINE 500
#define FOLD_LENGTH 15
size_t _getline(char s[], int lim);
/* _getline: read a line into s, return length */
size_t _getline(char s[], int lim) {
char c;
size_t i;
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
s[i] = c;
}
s[i] = '\0';
return i;
}
int main() {
char line[MAXLINE];
unsigned last_space_idx = 0;
unsigned i;
unsigned offset = 0;
while (_getline(line, MAXLINE) != 0) {
last_space_idx = 0;
offset = 0;
for (i = 0; line[offset+i] != '\0'; ++i) {
if (i == FOLD_LENGTH) {
printf("%.*s\n", last_space_idx, line + offset);
offset += last_space_idx + 1;
i = 0;
} else if (isspace(line[offset + i])) {
last_space_idx = i;
}
}
printf("%s\n", line + offset);
}
return 0;
}

Related

Write a program to break long input lines into two or more shorter lines of length at most n

I am currently learning C and working on a problem that breaks input lines into lengths of n. Below is my current code where n is set to 30. When it reaches the n-th index it replaces that index with ' ' and then line breaks, but it will only do it for the first n characters and I'm unsure what isn't getting rest in order to it to continue making a new line at the nth index.
int getline2(void);
int c, len, cut, counter;
char line[MAXLINE];
main() {
while ((len = getline2()) > 0) {
if (len > BREAK) {
c = 0;
counter = 0;
while (c < len) {
if (line[c] == ' ') {
counter = c;
}
if (counter == BREAK) {
line[counter] = '\n';
counter = 0;
}
counter++;
c++;
}
}
printf("%s", line);
}
return 0;
}
int getline2(void) {
int c, i;
extern char line[];
for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
line[i] = c; //i gets incremented at the end of the loop
if (c == '\n') {
line[i] = c;
++i;
}
line[i] = '\0';
return i;
}
Your code is a little too complicated:
you do not need to store the bytes read from the file into an array, just output them one at a time, keeping track of the line length
when the line would become too long, output a newline and reset the count before you output the byte.
also not that none of these global variables deserves to be global.
and the prototype for main should be either int main(), int main(void) or int main(int argc, char *argv[]) or equivalent. main()` is an obsolete syntax that should be avoided.
Here is a modified version:
#include <stdio.h>
#define BREAK 30
int main() {
int c;
int len = 0;
while ((c = getchar()) != EOF) {
if (c == '\n') {
putchar(c);
len = 0;
} else {
if (len >= BREAK) {
putchar('\n');
len = 0;
}
putchar(c);
len++;
}
}
return 0;
}

My program can't output the lines correctly

I'm relatively new in C and I currently reading Kernighan's book.
One of the problems in the book is to create an algorithm that from a input line output the line if it is more than 10 characters long.
The point is I'm frustrated because I cant find what is wrong with my code. I debugged and recreate it many times but still cant find out what's going on!
The escape character from function getl() is '.' (dot), and sometimes works and other times don't. If you compile it and test you will see:
gcc -Wall -o out 'script.c'
The question header from the book is:
“Exercise 1-17. Write a program to print all input lines that are longer than 10 characters.”
I'm sure that's relatively easy, but I really wanted to know why this algorithm is not working as expected, i think it has something to do with '\n'.
If someone could help me find out what's the problem with the code, I would appreciate it.
Code
#include <stdio.h>
#define MAX 10000
int getl(char line[], int lim) {
char c;
int count;
for (count = 0 ; count < lim-1 && (c = getchar()) != '.' ; count++) {
if (c == '\n') {
line[count] = '\n';
count++;
break;
}
line[count] = c;
}
line[count] = '\0';
return count;
}
int main() {
char line[MAX];
int len = 1;
for (; len > 0 ;) {
getl(line, MAX);
len = getl(line, MAX);
if (len >= 10)
printf("%s", line);
}
return 0;
}
Your code almost works. You just seem to have some repeated lines here and there that confuse things.
Specifically, you are calling getl(line, MAX); twice in a row. The first gets the input, but don't save the count, the second has only an empty stdin buffer to work with so no sensible count is saved from that. Removing the first call that don't save the count fixes your issue.
#include <stdio.h>
#define MAX 10000
int getl(char line[], int lim) {
char c = getchar();
int count;
for (count = 0 ; c != '.' ; count++) {
line[count] = c;
c = getchar();
}
line[count++] = '\n';
return count;
}
int main() {
char line[MAX];
int len = 1;
for (; len > 0 ;) {
len = getl(line, MAX);
if (len >= 10)
printf("%s", line);
}
return 0;
}
First, you're calling your getl function twice instead of once (you only want to read lines one by one). Fixing that should work.
Then I think you shouldn't add the trailing '\n' to your lines, just print it when your line is longer than 10 characters, in your code, the '\n' will be counted as a character.
Here's the modified code:
#include <stdlib.h>
#include <stdio.h>
#define MAX 10000
int getl(char line[])
{
char c;
int count;
for (count = 0; count < MAX - 1 && (c = getchar()) != '.' ; count++)
{
if (c == '\n')
break;
line[count] = c;
}
line[count] = '\0';
return (count);
}
int main()
{
char line[MAX];
int len = 1;
while (len > 0)
{
len = getl(line);
if (len >= 10)
printf("%s, c = %i\n", line, len);
}
return (0);
}
This should work. https://ideone.com/cXXRUH
#include <stdio.h>
#define MAX 10000
int getline_length(char line[]) {
char ch;
int count = 0;
printf("\nWaiting for INPUT...");
// Using clear while loop to get input, removing redundent complexity
// Either `.` or `\n` consider End Of Line
while(count < MAX-1 && ((ch = getchar()) != '.' || (ch = getchar()) != '\n')) {
line[count++]=ch;
}
line[count] = '\0';
return count;
}
int main() {
char line[MAX];
while(1) {
// reset array before each input
memset(line, 0, sizeof(line));
int len = getline_length(line); //No need to pass limit
if (len >= 10) {
printf("%s", line);
} else {
printf("len < 10");
}
}
return 0;
}

Why is this C program printing line longer than MAXLINE?

The program should print all the input lines which length is longer than MINLINE 5 and shorter than MAXLINE 10. Ref. K&R book exercise 1.17
#include <stdio.h>
#define MAXLINE 10
#define MINLINE 5
int getlines(char lines[], int maxline);
int main()
{
int length;
char lines[MAXLINE];
while ((length = getlines(lines, MAXLINE)) > 0)
{
if (length > MINLINE)
printf("%s", lines);
}
return 0;
}
int getlines(char lines[], int maxline)
{
int i, c;
for (i = 0; i < maxline - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
{
lines[i] = c;
}
if (c == '\n')
{
lines[i] = c;
++i;
}
lines[i] = '\0';
return i;
}
Desired outpur should be like this :-
Hello\n
Hello\n
hi\n
excuseMe\n
excuseMe\n
longLineNotToBePrinted\n
done
done
but unexpectedly the program printing lines that are far longer than MAXLINE and sometimes printing those omitting some trailing characters.
For starters this function
int getlines(char lines[], int maxline)
{
int i, c;
for (i = 0; i < maxline - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
{
lines[i] = c;
}
if (c == '\n')
{
lines[i] = c;
++i;
}
lines[i] = '\0';
return i;
}
has undefined behavior because it can store the character '\0' at position maxline that is outside the array lines that has the valid range of indices [0, maxline).
As for your question about the output then if you entered a text that is greater than maxline then the function will return a string that does not contain the new line character '\n'. So the next string will be outputted in the same line.
/* Updated code. Now it is working fine.
Problems were both in main() function and in the function getlines().
#include <stdio.h>
#define MAXLINE 10
#define MINLINE 5
int getlines(char lines[], int maxline);
main()
{
int length;
char lines[MAXLINE];
while ((length = getlines(lines, MAXLINE)) > 0)
{
if (length > MINLINE)
{
/* As the input line can be longer than MAXLINE and in that case there will be no '\n' escape sequence to be stored in the lines[MAXLINE] array so we have used the if block to flow the control in such a way that when the input line is longer than MAXLINE, the output string will be printed manually with a '\n' *newline character. */
if (length > MAXLINE)
printf("%s\n", lines);
else
printf("%s", lines);
}
}
return 0;
}
int getlines(char lines[], int maxline)
{
int i, j, c;
i = 0;
for (j = 0; (c = getchar()) != EOF && c != '\n'; ++j)
{
/* In the for loop this time we didn't use the condition 'j < maxline
-1' as getchar() needs to read the whole input line no matter it's length(can be greater than MAXLINE), rather we have used the 'j < maxline -1' condition as a nested if block inside the for loop. While doing this to keep the getchar() function busy reaching the last input character no matter how long the line is we have used two variable i and j to overcome the problem in such a way that i will be used to store characters in the lines[MAXLINE] array, while j will be increased untill it reaches the end of the line. */
if (j < maxline - 1)
{
i = j;
lines[i] = c;
++i;
}
}
if (c == '\n')
{
if (j < maxline - 1)
{
lines[i] = c;
++i;
++j;
}
else
++j;
}
lines[i] = '\0';
return j;
}

K&R 1.19 exercise ("reverse" func)

Here is the task:
Write a function reverse(s) that reverses the character string s . Use it to write a program that reverses its input a line at a time.
Ok, now, my performing:
#include <stdio.h>
#define LIM 40
char line[LIM];
int c;
int reverse(char line[], int lim);
int len;
int main(void) {
while ((len = reverse(line, LIM)) > 0) {
;
}
printf("\n END OF THE PROGRAM \n");
return 0;
}
********** THE REVERSE FUNCTION*********
int reverse(char s[], int lim) {
char rev[LIM];
int i;
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
int r;
for (r = 0; r < lim - 1; ++r) {
rev[r] = s[i];
--i;
}
int x;
for (x = 0; x < lim - 1; ++x) {
putchar(rev[x]);
}
printf("\n");
return r;
}
It seems to work correctly, but there are some bugs related to the output.
For example:
INPUT: hello everybody OUTPUT: ydobyreve olleh
INPUT: abc OUTPUT: cba'
INPUT: ABC OUTPUT: CBA'
INPUT: ABC ABC OUTPUT: CBA CBA
INPUT: se se OUTPUT: es es'
See? Some strange " ' " occurs in the end of output and I can't figure out any pattern why these "artifacts" get printed. It happens randomly (for me). Could you please suggest anything, what's wrong in the code?
Your reverse function has problems:
You should not store the newline into the s array as you do not want it to take part in the reverse operation.
You should stop the subsequent for loop when you reach the end of the string in s, not run all the way to the end of the buffer.
You should null terminate the rev array.
You do not need to output the rev array one character at a time, use is as a string.
Here is a corrected and simplified version:
#include <stdio.h>
#define LIM 40
int reverse(char line[], int size);
int main(void) {
char line[LIM];
int len;
while (reverse(line, LIM) > 0) {
continue;
}
printf("\n END OF THE PROGRAM \n");
return 0;
}
/* THE REVERSE FUNCTION */
int reverse(char s[], int size) {
char rev[size];
int i, r, c, len;
for (i = 0; i < size - 1 && (c = getchar()) != EOF && c != '\n'; i++) {
s[i] = c;
}
len = i;
s[i] = '\0';
for (i = 0; i < len; i++) {
rev[len - i - 1] = s[i];
}
rev[i] = '\0';
printf("%s\n", rev);
return len;
}
A bit more modular solution. I wasn't exactly sure what K&R meant by "one line at a time". But this will reverse the string until it finds a newline. Then wait for the user and repeat.
#include <stdio.h>
#define MAXLINE 1000
int get_line(char s[], int limit);
int reverse(char to[], char from[], int l);
int main() {
int size;
char line[MAXLINE];
while ((size = get_line(line, MAXLINE)) > 0) {
char revline[size];
int len = reverse(revline, line, size);
printf("%s\n", revline);
}
return 0;
}
int reverse(char to[], char from[], int l) {
int i;
int j = l - 2;
for (i = 0; i < l; i++, j--) {
to[i] = from[j];
}
to[i] = '\0';
return i;
}
// read a line into s until limit
// return length of line
int get_line(char s[], int limit) {
int c = 0;
int i = 0;
for (i = 0; i < limit-1 && (c = getchar()) != '\n'; ++i) {
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
Output:
testing one two
owt eno gnitset
three four
ruof eerht
five six
xis evif

Question mark at the end of the output

Trying to implement detab function which is described in K&R book http://clc-wiki.net/wiki/K%26R2_solutions:Chapter_1:Exercise_20 an faced a problem: when outputting a replaced string there's a question mark in the end of the output. Why?
#include <stdio.h>
#define MAXLINE 9999
void get_text(char output[]);
void detab(char input[], char output[], int tab_size);
int main()
{
char input[MAXLINE];
char final[MAXLINE];
get_text(input);
detab(input, final, 4);
for (int i = 0; final[i] != '\0'; ++i)
{
putchar(final[i]);
}
putchar('\n');
return 0;
}
void get_text(char output[])
{
int c;
int i = 0;
for (i = 0; i < MAXLINE && (c = getchar()) != EOF; ++i)
{
output[i] = c;
}
output[i + 1] = '\0';
}
void detab(char input[], char output[], int tab_size)
{
int c = 0;
int r = 0;
for (int i = 0; input[i] != '\0'; ++i)
{
c = input[i];
if(c == '\t')
{
for (int t = 0; t < tab_size; ++t)
{
output[r] = '.';
r++;
}
}
else
{
output[r] = c;
r++;
}
}
output[r] = '\0';
}
And here is the output when I'm passing the file with following content 'asdasdads tasdasdasdasdasd sadasdasd':
asdasdads....tasdasdasdasdasd....sadasdasd? (? at the end).
Why there is a question mark at the end?
output[i + 1] = '\0';
You don't need to add 1 here, it was already done in the loop. (First i is incremented and then i < MAXLINE && (c = getchar()) != EOF is tested, so i is one higher than in the last loop iteration already)
I'd guess that's in place of an unprintable character. The characters in final aren't initialized to anything in particular, and you're leaving a gap at the end of the detabbed string when you say:
output[i + 1] = '\0';
output[i], one past the output text, is still uninitialized.
You want:
output[i] = '\0';

Resources