Kernighan and Ritchie C exercise 1-18 - c

I was trying to complete the task requested by this exercise from the K&R book:
Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.
I still have to figure out how to implement the first task, but I have this idea for the second one:
#include <stdio.h>
#define MAXLINE 1000
#define IN 1
#define OUT 1
int blank1(char s[], int len){
int i;
int c=0;
for (i=0; i<len;i++){
if(s[i] == ' ' | s[i] == '\t')
c++;
}
if (c==(len-1))
return 1;
else
return 0;
}
int get_line1(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() {
char line[MAXLINE];
int len;
int j=1;
int i;
while(j){
len=get_line1(line,MAXLINE);
if ((blank1(line,len)) == 0) {
printf("%s", line);
}
if (getchar() == EOF){
j=0;
}
}
}
The function blank1 takes as input a string s[] and the string's length len.
Then it cycles for all the string and increases the counter i every time it hits a blank or tab. If the string is completely made of blank or tab, such as \t\t\t\n\0, then the value of i will be the same value of the length of the string - 1 (so to speak, the length of the string excluding the character \n). If the string is completely blank, then 1 will be returned, otherwise 0. So in this way if blank(string,string's length) returns 1, we know it is a wholly blank string and we can avoid printing it, as the exercise requests.
The problem is that with some outputs this program cuts the first letter. For instance with:
Once upon a time\n
There was a little monkey\n
That\n
What is printed is:
Once upon a time
here was a little monke
hat
I can't manage to get why this truncation occurs.
EDIT:
#include <stdio.h>
#define MAXLINE 1000
#define IN 1
#define OUT 1
int blank1(char s[], int len){
int i;
int c=0;
for (i=0; i<len;i++){
if(s[i] == ' ' | s[i] == '\t')
c++;
}
if (c==(len-1))
return 1;
else
return 0;
}
int get_line1(char s[], int lim){
int c,i;
for (i=0; i<lim-1 && ((c=getchar())!= EOF) && c!='\n'; i++){
s[i]=c;
}
if (c==EOF)
return -1; /////
if (c=='\n'){
s[i]=c;
i++;
}
s[i] = '\0';
return i;
}
int main() {
char line[MAXLINE];
char verso[MAXLINE];
char longest[MAXLINE];
int len;
int j=1;
int i;
while(j){
len=get_line1(line,MAXLINE);
if (len==-1){ ///////////
j=0;
}
else if ((blank1(line,len)) == 0) {
printf("%s", line);
}
}
}

getchar() == EOF in main is swallowing your first character from each line (after the first). You are better off calling getchar in only one place.
One possibility that causes minimal churn to your current implementation is to keep the only getchar call inside get_line1 and return -1 from get_line1 if a EOF is read there, to then be handled inside main, instead of the call to getchar inside main.

Do not program in main use functions for similar tasks.
char *skipLeadingBlanks(char *str)
{
char *wrk = str;
while((*wrk && *wrk != '\n') && (*wrk == ' ' || *wrk == '\t')) *wrk++;
memmove(str, wrk, strlen(wrk) + 1);
return str;
}
int main(void)
{
char str[1024];
while(fgets(str, sizeof(str), stdin))
{
skipLeadingBlanks(str);
if(!*str || *str == '\n') continue; //empty line
printf("%s",str);
}
}
https://godbolt.org/z/hxW5zP5Mx

Related

Why is this program assigning a weird number to the variable j?

I'm studying C with the Kernighan & Ritchie 2nd Ed. book and I'm currently at chapter 2. The fifth exercise in this chapter asks for the reader to "Write the function any(s1,s2), which returns the first location in a string s1 where any character from the string s2 occurs, or -1 if s1 contains no characters from s2."
I did my program, but I also went looking for others answers the see other people's approach to the problem. I came across this program found at this link.
#include<stdio.h>
#define MAXLINE 1000
int mgetline(char line[],int maxline);
int any(char s1[],char s2[]);
int main(void)
{
char s1[MAXLINE],s2[MAXLINE];
int val;
/* Give the first string s1 */
mgetline(s1,MAXLINE);
/* Give the second string s2 */
mgetline(s2,MAXLINE);
val = any(s1,s2);
printf("%d",val);
return 0;
}
int mgetline(char s[],int lim)
{
int i,c;
for(i=0;i<lim-1 && (c=getchar())!=EOF && c!='\n';++i)
s[i]=c;
if(c=='\n')
s[i++]=c;
s[i]='\0';
}
int any(char s1[],char s2[])
{
int i, j, check_next_char;
check_next_char = 1;
for(i = 0; s1[i] != '\0' && (check_next_char);)
{
// iterate through s2 while trying to find matching character from s1
for(j = 0; s2[j] != '\0' && (s1[i] != s2[j]); ++j)
; // continue
if(s2[j] == '\0') {
check_next_char=1;
i++; // go for the next char in s1
}
else
check_next_char=0; // match found
}
if(s1[i] == '\0')
return -1;
else
return i;
}
The issue is at the function any. Whenever I write strings that don't share any character the variable j gets assigned the value of 10 in the first loop, and then some random numbers as the loop proceeds. I can't find the issue with the code. What could be the reason for this behavior?
The new line char, '\n', is also considered as a character to be compared. According to your program, each string should have the new line char and it is matched at the end of the first string in any way. I fixed the problem by commenting out these two lines.
//if(c=='\n')
// s[i++]=c;
This is the program updated.
#include<stdio.h>
#define MAXLINE 1000
int mgetline(char line[],int maxline);
int any(char s1[],char s2[]);
int main(void)
{
char s1[MAXLINE],s2[MAXLINE];
int val;
/* Give the first string s1 */
mgetline(s1,MAXLINE);
/* Give the second string s2 */
mgetline(s2,MAXLINE);
val = any(s1,s2);
printf("%d",val);
return 0;
}
int mgetline(char s[],int lim)
{
int i,c;
for(i=0;i<lim-1 && (c=getchar())!=EOF && c!='\n';++i)
s[i]=c;
//if(c=='\n')
// s[i++]=c;
s[i]='\0';
}
int any(char s1[],char s2[])
{
int i, j, check_next_char;
check_next_char = 1;
for(i = 0; s1[i] != '\0' && (check_next_char);)
{
// iterate through s2 while trying to find matching character from s1
for(j = 0; s2[j] != '\0' && (s1[i] != s2[j]); ++j)
; // continue
if(s2[j] == '\0') {
check_next_char=1;
i++; // go for the next char in s1
}
else
check_next_char=0; // match found
}
if(s1[i] == '\0')
return -1;
else
return i;
}

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.

Only print input lines longer than 10 characters (ANSI C)

So I'm writing a practice program in C which has the purpose of taking user input and then after EOF is reached, it reads back the input but only lines that were longer than 10 characters.
I am on Linux, so EOF is Ctrl + D, but, if an input line is longer than 10, it prints when I push enter, rather than waiting until EOF is reached.
here is my code:
#define MAXSIZE 1000
#define SIZE 10
int checklen(char line[], int index);
int main()
{
char currentline[MAXSIZE];
int i = 0;
while ((currentline[i] = getchar()) != EOF){
if (currentline[i] == '\n'){
if (checklen(currentline, i) > SIZE){
printf("%s", currentline);
}
}
++i;
}
return 0;
}
int checklen(char line[], int index)
{
int i;
for (i=index; line[i] != '\n'; ++i){
;
}
return i;
}
EDIT: I have been trying to figure it out for quite a while now with no luck. I'm not really understanding what you guys are saying and everything but we'll get there eventually :)
I have since rewritten the code but it is still not working.
#include <stdio.h>
#define MAX 1000
#define SIZE 10
void examine(char input[], int index);
int main()
{
int i=0;
char input[MAX];
char output[MAX];
//take user input and store it in our input string
while ((input[i] = getchar()) != EOF){
++i;
}
//put a null byte at the end of input[]
input[i+1] = '\0';
//examine line by line until end of string (null byte)
for (i=0; input[i] != '\0'; ++i){
if (input[i] == '\n'){
examine(input, i);
}
}
return 0;
}
void examine(char input[], int index)
{
//decrement through input[] until \n or start [0] is reached
int i=0;
for (i=0; ((input[index] != '\n') || (index > 0)); ++i){
--index;
}
//if line is bigger than 10 chars, print it
if (i>SIZE){
for (; input[index+1] != '\n'; ++index){
putchar(input[index]);
}
}
//otherwise, return
return;
}
rewrote it. was actually really easy. here is the code:
/*this program takes keyboard input from the user until EOF
and prints out their input excluding lines less than or equal to LINESIZE*/
#include <stdio.h>
#define MAX 2000
#define LINESIZE 10
void checkprint(char line[]);
int main()
{
char input[MAX];
char line[MAX];
int i, i2;
i2 = 0;
//take input until EOF (Ctrl + D)
for (i=0; (input[i]=getchar()) != EOF; ++i){
;
}
//add null byte to end of string
input[i+1] = '\0';
//basic formatting for aesthetics
putchar('\n');
//copy a line into line[] from input[] until NULL byte reached
for (i=0; input[i] != '\0'; ++i){
line[i2] = input[i];
++i2;
//if end of line, call checkprint
if (input[i] == '\n'){
checkprint(line);
i2=0;
}
}
return 0;
}
void checkprint(char line[])
{
int i;
//find the length of the line
for (i=0; line[i] != '\n'; ++i){
;
}
//if longer than LINESIZE, print it
if (i > LINESIZE){
putchar('\n');
for (i=0; line[i] != '\n'; ++i){
putchar(line[i]);
}
}
}
#include <stdio.h>
#define MAX 1000
#define SIZE 10
void examine(char input[], int index);
int main(void){
char input[MAX];
// char output[MAX];
int i, ch;
for(i=0; i< MAX - 1 && (ch = getchar()) != EOF; ++i)
input[i] = ch;
input[i] = '\0';
for (i=0; input[i] != '\0'; ++i){
if (input[i] == '\n'){
examine(input, i);
}
}
examine(input, i);//for '\0'
return 0;
}
void examine(char input[], int index){
int i;
if(index == 0) return ;
for (i=1; index - i >= 0 && input[index-i] != '\n'; ++i)
;
--i;
if (i > SIZE){
while(i>0)
putchar(input[index - i--]);
putchar('\n');
}
return;
}
buffer's size 11 version.
#include <stdio.h>
#define SIZE 10
void print(char ch){
static char buf[SIZE+1];
static index = 0, over = 0;
int i;
if(over){
putchar(ch);
if(ch == '\n')
over = 0;
return ;
}
if(ch == '\n'){
index = 0;
} else {
buf[index++] = ch;
if(index == SIZE + 1){
for(i=0;i<index;++i){
putchar(buf[i]);
}
index = 0;
over = 1;
}
}
}
int main(void){
int ch;
while((ch = getchar()) != EOF){
print(ch);
}
return 0;
}
//simple is to use the fgets

Does this small C program satisfy the K&R exercise?

I'm on to K&R's Exercise 1-18
Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.
This is what I've came up with so far
#include <stdio.h>
#define MAXLINE 1000
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int main () {
int len;
char line[MAXLINE];
while (getline(line, MAXLINE) > 0) {
printf("%s", line);
}
return 0;
}
int getline(char s[], int lim) {
int c, i, lastNonBlankIndex;
lastNonBlankIndex = 0;
for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
if (c != ' ' && c != '\t') {
lastNonBlankIndex = i + 1;
}
s[i] = c;
}
if (i != lastNonBlankIndex) {
i = lastNonBlankIndex;
c = '\n';
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
The second part sounded hard, as I wasn't sure what I should return if the line only has blanks or tabs. After all, if I return 0, it will halt the getline() calling. Would this be where I should set up a #define, such as ALL_BLANKS.
Anyway, to actual main question, is this a correct way to remove trailing blanks and tabs from lines? I ran a few inputs through, and it seemed to work. However, if I copy and paste text with newlines into the CL, it appears all strung together. And when I type a line into the CL and push enter, it automatically prints it. Should I be building an array of lines, and then looping through and printing them when done ?
Your code looks correct, but I think it would be better if you separate the operations of reading a line from stdin and stripping the line of trailing whitespace (decoupling). Then you can use the unmodified getline from the book (code reuse) and won't have the problem of halting on returning 0.
And if you are interested in other solutions, the CLC-wiki has an almost complete list of K&R2 solutions.
#include <stdio.h>
#define MAXLINE 1024
int getline(char s[], int lim);
main()
{
int i, len;
char line[MAXLINE];
while ((len = getline(line, MAXLINE)) > 0) {
i = len - 2;
while (i >= 0 && (line[i] == ' ' || line[i] == '\t'))
--i;
if (i >= 0) {
line[i+1] = '\n';
line[i+2] = '\0';
printf("%s", line);
}
}
return 0;
}
This is the category 1 solution I wrote some time ago. getline is as on page 28 of the book. It might be nicer to put the removal of whitespace in a separate function rstrip, but I leave this as an exercise for the reader.
Your basic design is sound. It is better, as you did, to print a stripped line as soon as you've built it, so that your program only needs to keep one line at a time in memory and not the whole file.
There is a small problem with your code: it doesn't implement the second part of the question (“delete entirely blank line”). That's because you always tack a '\n' at the end of the string. This is easy to fix, but remember that you must return a nonzero value to your caller since a blank line doesn't indicate the end of the file.
getline should return -1 (a negative value in general) if there is an error or if EOF is reached. Then your loop conditional can check that it returns something >= 0 and still allow for 0 length lines.
for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
I almost never include an assignment within a loop conditional. I would rather add 10 lines of code to get around doing that because it's difficult to read. I would especially refrain from using them with complicated conditionals.
int i = 0;
while (i < lim) {
c = getchar();
if (c == EOF || c == '\n') {
break;
}
line[i] = (char)c;
i++;
}
line[i] = '\0'; // Null terminate the string
This code should read in a line for you. I would separate the reading in of the line from the removal of the trailing white space. You could very easily work backwards from the end of the string to remove white spaces at the location where I null terminated the line, since after having read in the line you now know its length. Essentially you grow the string and then you prune it back down after it has finished growing.
This is how i did it.
#include <stdio.h>
#define MAXLINE 1000
#define IN 1
#define OUT 0
int state = OUT;
int getline(char s[], int lim);
void copy(char to[], char from[]);
int main(void)
{
int lenght;
int max = 0;
char line[MAXLINE];
char longest[MAXLINE];
while ((lenght = getline(line, MAXLINE)) > 0)
if (lenght > max)
{
max = lenght;
copy(longest, line);
}
if (max > 0)
printf("\n%s\n", longest);
return 0;
}
int getline(char s[], int lim)
{
int i, c;
for (i = 0; i < lim - 1 && ((c = getchar()) != EOF) && (c != '\n'); i++)
{
if (state == IN && c != ' ' && c != '\t')
{
s[i] = ' ';
i++;
state = OUT;
}
if (s[0] == ' ')
{
s[0] = '\b';
}
s[i] = c;
if (c == ' ' || c == '\t')
{
i--;
state = IN;
}
}
if (c == '\n')
{
s[i] = c;
i++;
}
s[i] = '\0';
return i;
}
void copy(char to[], char from[])
{
int i = 0;
while ((to[i] = from[i]) != '\0')
i++;
}
#include <stdio.h>
#define MAXLINE 1000
size_t getline(char *s,size_t lim)
{
if( fgets(s,lim,stdin) )
{
while( *s && strchr(" \t\n",s[strlen(s)-1]) )
s[strlen(s)-1]=0;
return strlen(s);
}
return 0;
}
main()
{
int len;
char line[MAXLINE];
while (getline(line,sizeof line)) {
printf("%s", line);
}
return 0;
}

Resources