Exercise 1-20 of the book "C - The Programming Language 2nd Edition" - c

Currently I'm reading the book "C - The Programming Language" and I have a questions to this exercise:
"Write a program 'detab' that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n-colmuns. Should n be a variable or a symbolic parameter?"
Let aside the question in the exercise I wrote this program:
#include <stdio.h>
#define COLUMNS 5 /* number of columns for a tab */
int main()
{
char c;
int i;
while ((c = getchar()) != EOF) {
if (c == '\t') {
for (i = 0; i < COLUMNS; i++)
putchar(' ');
}
else
putchar(c);
}
return 0;
}
Then I checked Online for solutions and found this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BUFFER 1024
#define SPACE ' '
#define TAB '\t'
int CalculateNumberOfSpaces(int Offset, int TabSize)
{
return TabSize - (Offset % TabSize);
}
/* K&R's getline() function from p29 */
int getline(char s[], int lim)
{
int c, 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(void)
{
char Buffer[MAX_BUFFER];
int TabSize = 5; /* A good test value */
int i, j, k, l;
while(getline(Buffer, MAX_BUFFER) > 0)
{
for(i = 0, l = 0; Buffer[i] != '\0'; i++)
{
if(Buffer[i] == TAB)
{
j = CalculateNumberOfSpaces(l, TabSize);
for(k = 0; k < j; k++)
{
putchar(SPACE);
l++;
}
}
else
{
putchar(Buffer[i]);
l++;
}
}
}
return 0;
}
My question now is what the difference between my code an the other code is.
I thought the exercise would ask that each \t that occurs should be replaced by n spaces.
Now I don't what the other code does.
Maybe I understood the exercise wrong. If so please explain it to me.
Thanks for your help.
Ok so how about this solution:
#include <stdio.h>
#define TAB 8 /* size of a tab */
int main()
{
char c;
int i,column = 0;
while ((c = getchar()) != EOF) {
if (c == '\t') {
for (i = column; i < TAB; i++){
putchar(' ');
}
column = -1;
}
else{
putchar(c);
}
column++;
}
return 0;
}
New Version:
#include <stdio.h>
#define TAB 8 /* size of a tab */
int main()
{
char c;
int i,column = 0;
while ((c = getchar()) != EOF) {
if(c == '\n')
column = -1;
if(column >= TAB)
column = 0;
if (c == '\t') {
for (i = column; i < TAB; i++){
putchar(' ');
}
column = -1;
}
else{
putchar(c);
}
column++;
}
return 0;
}

You got it wrong. You have to replace each tab by spaces until the next tab stop. That depends exactly on where the cursor is.
So, if there's a tab stop every 5 columns, and you can think of it like:
-----|-----|-----|-----|-----|-----|-----|-----|-----|
Where | is a tab stop and - is a character. Hitting a tab takes you to the next tab stop. So, for example, if you write 3 characters from the beginning of the previous tab stop, and then hit tab, you only have to write 2 spaces.

You don't want to replace every tab by the same number of spaces: some will be replaced by less than that.
Imagine we want TAB to 10 spaces
1234567890123456789012345678901234567890
one<TAB> two<TAB> fortytwo four
^^^^^^^ ^^^^^^^ ^^
7 space 7 space 2

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;
}

Testing stored array characters in C

I have just started learning C and I am trying to complete an exercise where I have to remove all the trailing whitespace characters in a line. My logic seem fine to me, but when I try to test if a character is a tab or a space it doesn't come back true, even if it is.
The part with the problem:
if ((s[ii] == ' ' || s[ii] == '\t') && trailing == 1)
The entire thing:
#include <stdio.h>
#include <ctype.h>
#define MAXLINE 1000 /* maximum input line length */
int getline2(char line[], int maxline, char outputLine[]);
/* print above 80 character lines */
main()
{
char cl;
char sl[MAXLINE];
char ol[MAXLINE];
int i;
while((cl = getline2(sl, MAXLINE, ol)) > 0){
if(cl > 1){
printf("%d: %s", cl, ol);
}
}
}
/* getline: read a line into s, return length */
int getline2(char s[], int lim, char ss[])
{
int c, i, ii;
for (i=0; i < lim-1 && (c=getchar())!=EOF && c!= '\n'; ++i){
s[i] = c;
}
int trailing = 1;
for(ii = i; ii >= 0; ii--){
if ((s[ii] == ' ' || s[ii] == '\t') && trailing == 1){
i--;
} else {
ss[ii] = s[ii];
trailing = 0;
}
}
if (c == '\n') {
ss[i] = c;
i++;
}
ss[i] = '\0';
return i;
}
This for loop should start from i - 1:
for(ii = i - 1; ii >= 0; ii--){
^^^^^
because in previous for loop, i is incremented after adding the trailing spaces and tabs in the string s.

Input string of unknown length

I wrote this program that replaces two spaces with an '*'.
How do I modify the code so that it does the same thing regardless of the string size? Is it even possible only using putchar and getchar?
#include <stdio.h>
int c;
char buffer[256];
int counter= 0;
int i;
int main()
{
while ((c = getchar()) != '\n'&&c!=EOF) {
buffer[counter] =c;
counter++;
if (counter >=255) {
break;
}
}
for(i=0; i<256; i++) {
if(buffer[i]== ' '&&buffer[i+1]==' ')
{
buffer[i]= '*';
putchar(buffer[i]);
i = i + 2;
continue;
}
putchar(buffer[i]);
}
putchar('\n');
return 0;
}
The problem statement doesn't require you to store the complete input in a buffer. The decision on what character to output only depends on the last two characters of input. Consider the following code:
#include <stdio.h>
int main(void)
{
// two variables for the last two input characters
int c = EOF, last = EOF;
while ((c = getchar()) != EOF)
{
// if both are a space, store a single '*' instead
if (c == ' ' && last == ' ')
{
c = '*';
last = EOF;
}
// print the output, and shift the buffer
if (last != EOF)
putchar(last);
last = c;
}
// take care of the last character in the buffer after we see EOF
if (last != EOF)
putchar(last);
}
no need for malloc and friends at all. This is a good expample for a problem that requires you to think carefully, before writing code, in order to not waste unnecessary resources on buffers.
Code for just printing:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char prev = EOF, curr;
while ((curr =(char)getchar()) != '\n' && curr != EOF)
{
if(curr==' '&&prev==' ')
{
curr = '*';
prev = EOF;
}
if (prev != EOF)
putchar(prev);
prev = curr;
}
putchar(prev);
return 0;
}
Using realloc for actually changing the string:
#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned int len_max = 128;
char *m = malloc(len_max);
char c;
int counter= 0;
int i;
int current_size = 256;
printf("please input a string\n");
while ((c = getchar()) != '\n' && c != EOF)
{
m[counter] =(char)c;
counter++;
if(counter == current_size)
{
current_size = i+len_max;
m = realloc(m, current_size);
}
}
for(i=0; i<counter; i++)
{
if(m[i]== ' '&&m[i+1]==' ')
{
m[i]= '*';
putchar(m[i]);
i = i + 2;
continue;
}
putchar(m[i]);
}
putchar('\n');
free(m);
m = NULL;
return 0;
}

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

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;
}

Histogram of the length of words exercise hint?

I'm learning C with "The C Programming Language" book, and I'm trying to solve exercise 1.13:
"Write a program to print a histogram of the lengths of words in its input. It is easy to
draw the histogram with the bars horizontal; a vertical orientation is more challenging."
I wrote the code, but when I press CTRL+Z (End-of-File), it shows all zeros instead of the length of words.
Could anyone give me a hint on where I'm going wrong?
#include <stdio.h>
/* print a histogram of the length of words from input */
main()
{
int c, i, wordn, space;
int lengthn[20];
wordn = space = 0;
for (i = 0; i < 20; ++i)
lengthn[i] = 0;
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\t' || c == '\n')
if (space == 1) {
++wordn;
space = 0;
++i;
}
if (c != ' ' && c != '\t' && c != '\n') {
++lengthn[i];
space = 1;
}
}
printf("Length: ");
for (i = 0; i < 16; ++i)
printf("%d ", lengthn[i]);
printf("\n --------------------------------------------------------------\n");
printf("Word: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\n");
}
(Because the OP is asking for hints, not the solution)
So ... what does i equal after this loop?
for (i = 0; i < 20; ++i)
lengthn[i] = 0;
And where do you use it next?
for (i = 0; i < 20; ++i)
lengthn[i] = 0;
The value of i after this loop will be i=20
so you must initialize i before while loop
I wrote a code for the vertical orientation.
I'm new to C, so may be the code is not good.
#include <stdio.h>
#include <conio.h>
#define MAX_WORDS 100
#define IN 1
#define OUT 0
int maxlength(int length[], char num_of_word);
int main()
{
char c,i,j,state,num_of_word;
int length[MAX_WORDS];
/*initialize length[]*/
for(i=0;i<MAX_WORDS;i++){
length[i]=0;
}
/* find the length of each word */
num_of_word=0;
while(num_of_word<MAX_WORDS && (c = getchar()) != EOF && c != 'a'){
if(c != ' ' && c!= '\t' && c!= '\n'){
state = IN;
length[num_of_word]++;
}else{
if(state != OUT){
state = OUT;
num_of_word++;
}
}
}
/* draw histogram */
for(i= maxlength(length[],num_of_word);i>0;i--){
for(j=0;j<num_of_word;j++){
if(length[j]<i){
printf(" ");
}else{
printf("|");
}
}
printf("\n");
}
/* print name of each column*/
for(i=0;i<num_of_word;i++){
printf("%d",i+1);
}
_getch();
return(0);
}
/*sub-function that find the longest word */
int maxlength(int length[], char num_of_word){
int i, max;
max = length[0];
for(i=1;i<num_of_word;i++){
if(max<length[i]){
max = length[i];
}
}
return max;
}

Resources