K&R Exercise 1-18. Remove trailing blanks and tabs from each line












5














Intro



I'm going through the K&R book (2nd edition, ANSI C ver.) and want to get the most from it: learn (outdated) C and practice problem-solving at the same time. I believe that the author's intention was to give the reader a good exercise, to make him think hard about what he can do with the tools introduced, so I'm sticking to program features introduced so far and using "future" features and standards only if they don't change the program logic.



Compiling with gcc -Wall -Wextra -Wconversion -pedantic -std=c99.



K&R Exercise 1-18



Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.



Solution



My solution reuses functions coded in the previous exercises (getline & copy) and adds a new function size_t trimtrail(char line); to solve the problem. For lines that can fit in the buffer, the solution is straightforward. However, what if they can't? The main routine deals with that.



Since dynamic memory allocation hasn't been introduced, I don't see a way to completely trim arbitrary length lines. Therefore, solution does the next best thing: trim the ends, and signal whether there's more job to be done. This way, the shell can re-run the program as many times as necessary to finish the job.



Code



/* Exercise 1-18. Write a program to remove trailing blanks and tabs
* from each line of input, and to delete entirely blank lines.
*/

#include <stdio.h>
#include <stdbool.h>
#define BUFSIZE 10 // line buffer size

size_t getline(char line, size_t sz);
void copy(char to, char from);
size_t trimtrail(char line);

int main(void)
{
size_t len; // working length
size_t nlen; // peek length
size_t tlen; // trimmed length
char line[BUFSIZE]; // working buffer
char nline[BUFSIZE]; // peek buffer
bool istail = false;
bool ismore = false;

len = getline(line, BUFSIZE);
while (len > 0) {
if (line[len-1] == 'n') {
// proper termination can mean either a whole line, or end
// of one
tlen = trimtrail(line);
if (istail == false) {
// base case, whole line fits in the working buffer
// print only non-empty lines
if (line[0] != 'n') {
printf("%s", line);
}
}
else {
// long line case, only the tail in the working buffer
printf("%s", line);
if (len != tlen) {
// we couldn't keep the whole history so maybe more
// blanks were seen which could not be processed;
// run the program again to catch those
ismore = true;
}
}
// this always gets the [beginning of] next line
len = getline(line, BUFSIZE);
istail = 0;
}
else {
// if it was not properly terminated, peek ahead to
// determine whether there's more of the line or we reached
// EOF
nlen = getline(nline, BUFSIZE);
if (nlen > 0) {
if (nline[0]=='n') {
// if next read got us just the 'n'
// we can safely trim the preceding buffer
tlen = trimtrail(line);
if (tlen > 0) {
printf("%s", line);
if (len != tlen)
ismore = 1;
}
}
else {
// if still no 'n', we don't know if safe to trim
// and can only print the preceding buffer here
printf("%s", line);
}
// we didn't yet process the 2nd buffer so copy it into
// 1st and run it through the loop above
len = nlen;
copy(line, nline);
istail = 1;
}
else {
// EOF reached, peek buffer empty
// means we can safely trim the preceding buffer
tlen = trimtrail(line);
if (tlen > 0) {
if (line[0]!='n') {
printf("%s", line);
}
else {
ismore = 1;
}
}
if (len != tlen) {
ismore = 1;
}
// and we don't need to run the loop anymore, exit here
len = 0;
}
}
}
// if there were too long lines, we could not trim them all;
// signal to the environment that more runs could be required
return ismore;
}

/* getline: read a line into `s`, return string length;
* `sz` must be >1 to accomodate at least one character and string
* termination ''
*/
size_t getline(char s, size_t sz)
{
int c;
size_t i = 0;
bool el = false;
while (i < sz-1 && el == false) {
c = getchar();
if (c == EOF) {
el = true;
}
else {
s[i] = (char) c;
++i;
if (c == 'n') {
el = true;
}
}
}
if (i < sz) {
s[i] = '';
}
return i;
}

/* copy: copy a '' terminated string `from` into `to`;
* assume `to` is big enough;
*/
void copy(char to, char from)
{
size_t i;
for (i = 0; from[i] != ''; ++i) {
to[i] = from[i];
}
to[i] = '';
}

/* trimtrail: trim trailing tabs and blanks, returns new length
*/
size_t trimtrail(char s)
{
size_t lastnb;
size_t i;
// find the last non-blank char
for (i = 0, lastnb = 0; s[i] != ''; ++i) {
if (s[i] != ' ' && s[i] != 't' && s[i] != 'n') {
lastnb = i;
}
}
// is it a non-empty string?
if (i > 0) {
--i;
// is there a non-blank char?
if (lastnb > 0 ||
(s[0] != ' ' && s[0] != 't' && s[0] != 'n')) {
// has non-blanks, but is it properly terminated?
if (s[i] == 'n') {
++lastnb;
s[lastnb] = 'n';
}
}
else {
// blanks-only line, but is it properly terminated?
if (s[i] == 'n') {
s[lastnb] = 'n';
}
}
++lastnb;
s[lastnb] = '';
return lastnb;
}
else {
// empty string
return 0;
}
}


Test



Input File



1        
2



444 4
5555 5
66666 6 6
777777 7
8888888 8
99999999 9
000000000 0
1


Test Script (Bash)



i=0
j=1
./ch1-ex-1-18-01 <test.txt >out1.txt
while [ $? -eq 1 ] && [ $j -lt 20 ]; do
let i+=1
let j+=1
./ch1-ex-1-18-01 <out${i}.txt >out${j}.txt
done









share|improve this question



























    5














    Intro



    I'm going through the K&R book (2nd edition, ANSI C ver.) and want to get the most from it: learn (outdated) C and practice problem-solving at the same time. I believe that the author's intention was to give the reader a good exercise, to make him think hard about what he can do with the tools introduced, so I'm sticking to program features introduced so far and using "future" features and standards only if they don't change the program logic.



    Compiling with gcc -Wall -Wextra -Wconversion -pedantic -std=c99.



    K&R Exercise 1-18



    Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.



    Solution



    My solution reuses functions coded in the previous exercises (getline & copy) and adds a new function size_t trimtrail(char line); to solve the problem. For lines that can fit in the buffer, the solution is straightforward. However, what if they can't? The main routine deals with that.



    Since dynamic memory allocation hasn't been introduced, I don't see a way to completely trim arbitrary length lines. Therefore, solution does the next best thing: trim the ends, and signal whether there's more job to be done. This way, the shell can re-run the program as many times as necessary to finish the job.



    Code



    /* Exercise 1-18. Write a program to remove trailing blanks and tabs
    * from each line of input, and to delete entirely blank lines.
    */

    #include <stdio.h>
    #include <stdbool.h>
    #define BUFSIZE 10 // line buffer size

    size_t getline(char line, size_t sz);
    void copy(char to, char from);
    size_t trimtrail(char line);

    int main(void)
    {
    size_t len; // working length
    size_t nlen; // peek length
    size_t tlen; // trimmed length
    char line[BUFSIZE]; // working buffer
    char nline[BUFSIZE]; // peek buffer
    bool istail = false;
    bool ismore = false;

    len = getline(line, BUFSIZE);
    while (len > 0) {
    if (line[len-1] == 'n') {
    // proper termination can mean either a whole line, or end
    // of one
    tlen = trimtrail(line);
    if (istail == false) {
    // base case, whole line fits in the working buffer
    // print only non-empty lines
    if (line[0] != 'n') {
    printf("%s", line);
    }
    }
    else {
    // long line case, only the tail in the working buffer
    printf("%s", line);
    if (len != tlen) {
    // we couldn't keep the whole history so maybe more
    // blanks were seen which could not be processed;
    // run the program again to catch those
    ismore = true;
    }
    }
    // this always gets the [beginning of] next line
    len = getline(line, BUFSIZE);
    istail = 0;
    }
    else {
    // if it was not properly terminated, peek ahead to
    // determine whether there's more of the line or we reached
    // EOF
    nlen = getline(nline, BUFSIZE);
    if (nlen > 0) {
    if (nline[0]=='n') {
    // if next read got us just the 'n'
    // we can safely trim the preceding buffer
    tlen = trimtrail(line);
    if (tlen > 0) {
    printf("%s", line);
    if (len != tlen)
    ismore = 1;
    }
    }
    else {
    // if still no 'n', we don't know if safe to trim
    // and can only print the preceding buffer here
    printf("%s", line);
    }
    // we didn't yet process the 2nd buffer so copy it into
    // 1st and run it through the loop above
    len = nlen;
    copy(line, nline);
    istail = 1;
    }
    else {
    // EOF reached, peek buffer empty
    // means we can safely trim the preceding buffer
    tlen = trimtrail(line);
    if (tlen > 0) {
    if (line[0]!='n') {
    printf("%s", line);
    }
    else {
    ismore = 1;
    }
    }
    if (len != tlen) {
    ismore = 1;
    }
    // and we don't need to run the loop anymore, exit here
    len = 0;
    }
    }
    }
    // if there were too long lines, we could not trim them all;
    // signal to the environment that more runs could be required
    return ismore;
    }

    /* getline: read a line into `s`, return string length;
    * `sz` must be >1 to accomodate at least one character and string
    * termination ''
    */
    size_t getline(char s, size_t sz)
    {
    int c;
    size_t i = 0;
    bool el = false;
    while (i < sz-1 && el == false) {
    c = getchar();
    if (c == EOF) {
    el = true;
    }
    else {
    s[i] = (char) c;
    ++i;
    if (c == 'n') {
    el = true;
    }
    }
    }
    if (i < sz) {
    s[i] = '';
    }
    return i;
    }

    /* copy: copy a '' terminated string `from` into `to`;
    * assume `to` is big enough;
    */
    void copy(char to, char from)
    {
    size_t i;
    for (i = 0; from[i] != ''; ++i) {
    to[i] = from[i];
    }
    to[i] = '';
    }

    /* trimtrail: trim trailing tabs and blanks, returns new length
    */
    size_t trimtrail(char s)
    {
    size_t lastnb;
    size_t i;
    // find the last non-blank char
    for (i = 0, lastnb = 0; s[i] != ''; ++i) {
    if (s[i] != ' ' && s[i] != 't' && s[i] != 'n') {
    lastnb = i;
    }
    }
    // is it a non-empty string?
    if (i > 0) {
    --i;
    // is there a non-blank char?
    if (lastnb > 0 ||
    (s[0] != ' ' && s[0] != 't' && s[0] != 'n')) {
    // has non-blanks, but is it properly terminated?
    if (s[i] == 'n') {
    ++lastnb;
    s[lastnb] = 'n';
    }
    }
    else {
    // blanks-only line, but is it properly terminated?
    if (s[i] == 'n') {
    s[lastnb] = 'n';
    }
    }
    ++lastnb;
    s[lastnb] = '';
    return lastnb;
    }
    else {
    // empty string
    return 0;
    }
    }


    Test



    Input File



    1        
    2



    444 4
    5555 5
    66666 6 6
    777777 7
    8888888 8
    99999999 9
    000000000 0
    1


    Test Script (Bash)



    i=0
    j=1
    ./ch1-ex-1-18-01 <test.txt >out1.txt
    while [ $? -eq 1 ] && [ $j -lt 20 ]; do
    let i+=1
    let j+=1
    ./ch1-ex-1-18-01 <out${i}.txt >out${j}.txt
    done









    share|improve this question

























      5












      5








      5







      Intro



      I'm going through the K&R book (2nd edition, ANSI C ver.) and want to get the most from it: learn (outdated) C and practice problem-solving at the same time. I believe that the author's intention was to give the reader a good exercise, to make him think hard about what he can do with the tools introduced, so I'm sticking to program features introduced so far and using "future" features and standards only if they don't change the program logic.



      Compiling with gcc -Wall -Wextra -Wconversion -pedantic -std=c99.



      K&R Exercise 1-18



      Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.



      Solution



      My solution reuses functions coded in the previous exercises (getline & copy) and adds a new function size_t trimtrail(char line); to solve the problem. For lines that can fit in the buffer, the solution is straightforward. However, what if they can't? The main routine deals with that.



      Since dynamic memory allocation hasn't been introduced, I don't see a way to completely trim arbitrary length lines. Therefore, solution does the next best thing: trim the ends, and signal whether there's more job to be done. This way, the shell can re-run the program as many times as necessary to finish the job.



      Code



      /* Exercise 1-18. Write a program to remove trailing blanks and tabs
      * from each line of input, and to delete entirely blank lines.
      */

      #include <stdio.h>
      #include <stdbool.h>
      #define BUFSIZE 10 // line buffer size

      size_t getline(char line, size_t sz);
      void copy(char to, char from);
      size_t trimtrail(char line);

      int main(void)
      {
      size_t len; // working length
      size_t nlen; // peek length
      size_t tlen; // trimmed length
      char line[BUFSIZE]; // working buffer
      char nline[BUFSIZE]; // peek buffer
      bool istail = false;
      bool ismore = false;

      len = getline(line, BUFSIZE);
      while (len > 0) {
      if (line[len-1] == 'n') {
      // proper termination can mean either a whole line, or end
      // of one
      tlen = trimtrail(line);
      if (istail == false) {
      // base case, whole line fits in the working buffer
      // print only non-empty lines
      if (line[0] != 'n') {
      printf("%s", line);
      }
      }
      else {
      // long line case, only the tail in the working buffer
      printf("%s", line);
      if (len != tlen) {
      // we couldn't keep the whole history so maybe more
      // blanks were seen which could not be processed;
      // run the program again to catch those
      ismore = true;
      }
      }
      // this always gets the [beginning of] next line
      len = getline(line, BUFSIZE);
      istail = 0;
      }
      else {
      // if it was not properly terminated, peek ahead to
      // determine whether there's more of the line or we reached
      // EOF
      nlen = getline(nline, BUFSIZE);
      if (nlen > 0) {
      if (nline[0]=='n') {
      // if next read got us just the 'n'
      // we can safely trim the preceding buffer
      tlen = trimtrail(line);
      if (tlen > 0) {
      printf("%s", line);
      if (len != tlen)
      ismore = 1;
      }
      }
      else {
      // if still no 'n', we don't know if safe to trim
      // and can only print the preceding buffer here
      printf("%s", line);
      }
      // we didn't yet process the 2nd buffer so copy it into
      // 1st and run it through the loop above
      len = nlen;
      copy(line, nline);
      istail = 1;
      }
      else {
      // EOF reached, peek buffer empty
      // means we can safely trim the preceding buffer
      tlen = trimtrail(line);
      if (tlen > 0) {
      if (line[0]!='n') {
      printf("%s", line);
      }
      else {
      ismore = 1;
      }
      }
      if (len != tlen) {
      ismore = 1;
      }
      // and we don't need to run the loop anymore, exit here
      len = 0;
      }
      }
      }
      // if there were too long lines, we could not trim them all;
      // signal to the environment that more runs could be required
      return ismore;
      }

      /* getline: read a line into `s`, return string length;
      * `sz` must be >1 to accomodate at least one character and string
      * termination ''
      */
      size_t getline(char s, size_t sz)
      {
      int c;
      size_t i = 0;
      bool el = false;
      while (i < sz-1 && el == false) {
      c = getchar();
      if (c == EOF) {
      el = true;
      }
      else {
      s[i] = (char) c;
      ++i;
      if (c == 'n') {
      el = true;
      }
      }
      }
      if (i < sz) {
      s[i] = '';
      }
      return i;
      }

      /* copy: copy a '' terminated string `from` into `to`;
      * assume `to` is big enough;
      */
      void copy(char to, char from)
      {
      size_t i;
      for (i = 0; from[i] != ''; ++i) {
      to[i] = from[i];
      }
      to[i] = '';
      }

      /* trimtrail: trim trailing tabs and blanks, returns new length
      */
      size_t trimtrail(char s)
      {
      size_t lastnb;
      size_t i;
      // find the last non-blank char
      for (i = 0, lastnb = 0; s[i] != ''; ++i) {
      if (s[i] != ' ' && s[i] != 't' && s[i] != 'n') {
      lastnb = i;
      }
      }
      // is it a non-empty string?
      if (i > 0) {
      --i;
      // is there a non-blank char?
      if (lastnb > 0 ||
      (s[0] != ' ' && s[0] != 't' && s[0] != 'n')) {
      // has non-blanks, but is it properly terminated?
      if (s[i] == 'n') {
      ++lastnb;
      s[lastnb] = 'n';
      }
      }
      else {
      // blanks-only line, but is it properly terminated?
      if (s[i] == 'n') {
      s[lastnb] = 'n';
      }
      }
      ++lastnb;
      s[lastnb] = '';
      return lastnb;
      }
      else {
      // empty string
      return 0;
      }
      }


      Test



      Input File



      1        
      2



      444 4
      5555 5
      66666 6 6
      777777 7
      8888888 8
      99999999 9
      000000000 0
      1


      Test Script (Bash)



      i=0
      j=1
      ./ch1-ex-1-18-01 <test.txt >out1.txt
      while [ $? -eq 1 ] && [ $j -lt 20 ]; do
      let i+=1
      let j+=1
      ./ch1-ex-1-18-01 <out${i}.txt >out${j}.txt
      done









      share|improve this question













      Intro



      I'm going through the K&R book (2nd edition, ANSI C ver.) and want to get the most from it: learn (outdated) C and practice problem-solving at the same time. I believe that the author's intention was to give the reader a good exercise, to make him think hard about what he can do with the tools introduced, so I'm sticking to program features introduced so far and using "future" features and standards only if they don't change the program logic.



      Compiling with gcc -Wall -Wextra -Wconversion -pedantic -std=c99.



      K&R Exercise 1-18



      Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.



      Solution



      My solution reuses functions coded in the previous exercises (getline & copy) and adds a new function size_t trimtrail(char line); to solve the problem. For lines that can fit in the buffer, the solution is straightforward. However, what if they can't? The main routine deals with that.



      Since dynamic memory allocation hasn't been introduced, I don't see a way to completely trim arbitrary length lines. Therefore, solution does the next best thing: trim the ends, and signal whether there's more job to be done. This way, the shell can re-run the program as many times as necessary to finish the job.



      Code



      /* Exercise 1-18. Write a program to remove trailing blanks and tabs
      * from each line of input, and to delete entirely blank lines.
      */

      #include <stdio.h>
      #include <stdbool.h>
      #define BUFSIZE 10 // line buffer size

      size_t getline(char line, size_t sz);
      void copy(char to, char from);
      size_t trimtrail(char line);

      int main(void)
      {
      size_t len; // working length
      size_t nlen; // peek length
      size_t tlen; // trimmed length
      char line[BUFSIZE]; // working buffer
      char nline[BUFSIZE]; // peek buffer
      bool istail = false;
      bool ismore = false;

      len = getline(line, BUFSIZE);
      while (len > 0) {
      if (line[len-1] == 'n') {
      // proper termination can mean either a whole line, or end
      // of one
      tlen = trimtrail(line);
      if (istail == false) {
      // base case, whole line fits in the working buffer
      // print only non-empty lines
      if (line[0] != 'n') {
      printf("%s", line);
      }
      }
      else {
      // long line case, only the tail in the working buffer
      printf("%s", line);
      if (len != tlen) {
      // we couldn't keep the whole history so maybe more
      // blanks were seen which could not be processed;
      // run the program again to catch those
      ismore = true;
      }
      }
      // this always gets the [beginning of] next line
      len = getline(line, BUFSIZE);
      istail = 0;
      }
      else {
      // if it was not properly terminated, peek ahead to
      // determine whether there's more of the line or we reached
      // EOF
      nlen = getline(nline, BUFSIZE);
      if (nlen > 0) {
      if (nline[0]=='n') {
      // if next read got us just the 'n'
      // we can safely trim the preceding buffer
      tlen = trimtrail(line);
      if (tlen > 0) {
      printf("%s", line);
      if (len != tlen)
      ismore = 1;
      }
      }
      else {
      // if still no 'n', we don't know if safe to trim
      // and can only print the preceding buffer here
      printf("%s", line);
      }
      // we didn't yet process the 2nd buffer so copy it into
      // 1st and run it through the loop above
      len = nlen;
      copy(line, nline);
      istail = 1;
      }
      else {
      // EOF reached, peek buffer empty
      // means we can safely trim the preceding buffer
      tlen = trimtrail(line);
      if (tlen > 0) {
      if (line[0]!='n') {
      printf("%s", line);
      }
      else {
      ismore = 1;
      }
      }
      if (len != tlen) {
      ismore = 1;
      }
      // and we don't need to run the loop anymore, exit here
      len = 0;
      }
      }
      }
      // if there were too long lines, we could not trim them all;
      // signal to the environment that more runs could be required
      return ismore;
      }

      /* getline: read a line into `s`, return string length;
      * `sz` must be >1 to accomodate at least one character and string
      * termination ''
      */
      size_t getline(char s, size_t sz)
      {
      int c;
      size_t i = 0;
      bool el = false;
      while (i < sz-1 && el == false) {
      c = getchar();
      if (c == EOF) {
      el = true;
      }
      else {
      s[i] = (char) c;
      ++i;
      if (c == 'n') {
      el = true;
      }
      }
      }
      if (i < sz) {
      s[i] = '';
      }
      return i;
      }

      /* copy: copy a '' terminated string `from` into `to`;
      * assume `to` is big enough;
      */
      void copy(char to, char from)
      {
      size_t i;
      for (i = 0; from[i] != ''; ++i) {
      to[i] = from[i];
      }
      to[i] = '';
      }

      /* trimtrail: trim trailing tabs and blanks, returns new length
      */
      size_t trimtrail(char s)
      {
      size_t lastnb;
      size_t i;
      // find the last non-blank char
      for (i = 0, lastnb = 0; s[i] != ''; ++i) {
      if (s[i] != ' ' && s[i] != 't' && s[i] != 'n') {
      lastnb = i;
      }
      }
      // is it a non-empty string?
      if (i > 0) {
      --i;
      // is there a non-blank char?
      if (lastnb > 0 ||
      (s[0] != ' ' && s[0] != 't' && s[0] != 'n')) {
      // has non-blanks, but is it properly terminated?
      if (s[i] == 'n') {
      ++lastnb;
      s[lastnb] = 'n';
      }
      }
      else {
      // blanks-only line, but is it properly terminated?
      if (s[i] == 'n') {
      s[lastnb] = 'n';
      }
      }
      ++lastnb;
      s[lastnb] = '';
      return lastnb;
      }
      else {
      // empty string
      return 0;
      }
      }


      Test



      Input File



      1        
      2



      444 4
      5555 5
      66666 6 6
      777777 7
      8888888 8
      99999999 9
      000000000 0
      1


      Test Script (Bash)



      i=0
      j=1
      ./ch1-ex-1-18-01 <test.txt >out1.txt
      while [ $? -eq 1 ] && [ $j -lt 20 ]; do
      let i+=1
      let j+=1
      ./ch1-ex-1-18-01 <out${i}.txt >out${j}.txt
      done






      beginner c strings io






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 2 at 15:47









      div0man

      2119




      2119






















          1 Answer
          1






          active

          oldest

          votes


















          4














          Review covers only minor stuff.



          getline()



          Avoid a technical exploit when size == 0. Although this code passes sizes more than 0, the function is hackable with size == 0.



          When sz == 0, as type size_t, sz-1 is a huge value. Simply + 1 on the left-hand side instead.



          // while (i < sz-1 && el == false)
          while (i + 1 < sz && el == false)


          Advanced: getline()



          When a rare reading error occurs, getchar() returns EOF. Standard functions like fgets() return NULL even if some characters were successfully read prior to the error. This differs from OP's getline() functionality. Since getline() uses a return of 0 to indicate end-of-file (and no data read), a parallel functionality to fgets() would also return 0 when an input error occurs (even if some good data read prior).



          Easy, yet pedantic, change suggested:



          if (i < sz) {
          // add if
          if (c == EOF && !feof(stdin)) { // EOF due to error
          i = 0;
          }
          s[i] = '';
          }


          Consider const



          When the source data does not change, using const can make for 1) more clarity in function usage 2) greater applicability as then const char *f; copy(..., f); is possible. 3) potentially more efficient code.



          // void copy(char to, char from);
          void copy(char to, char const from);


          Advanced: Consider restrict



          restrict, roughly, implies that the data referenced by pointer only changes due to the code's function without side effects. Should from/to overlap, copy() as presently coded, can dramatically fail. restrict informs the caller that to/from should not overlap and thus allows the compiler to perform additional optimizations based on that.



          // void copy(char to, char const from);
          void copy(char * restrict to, char const * restrict from);


          Inconsistent documentation/function



          Code is described as "trim trailing tabs and blanks" yet then trims ' ', 't' and 'n'. Recommended consistent documentation and function.



          Sentinels



          When printing string test output, especially ones with white-space removal, use sentinels to help show problems.



          // printf("%s", line);
          printf("<%s>", line);


          bool deserves boolean syntax



          Style issue.



          // while (i < sz-1 && el == false) {
          while (i < sz-1 && !el) {




          No major issues noted. Well done.






          share|improve this answer























          • Thanks! So the EOF test is for when just 1 getchar call returned EOF, but next call would return something else. Currently my program would still read the partial line, and continue reading after the error. With the fix, it would stop at the error, is that right? As for the "trim trailing tabs and blanks", if the string had an n, it puts it back at the end, if the string didn't have it (too long line, or EOF-terminated), it doesn't add it.
            – div0man
            Nov 4 at 10:22












          • @div0man ,"it would stop at the error, is that right? " Yes.
            – chux
            Nov 5 at 6:32










          • Great review; should be pointed out that bool and restrict are a C99 feature and not C90.
            – Neil Edelman
            Nov 7 at 23:09













          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f206811%2fkr-exercise-1-18-remove-trailing-blanks-and-tabs-from-each-line%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          4














          Review covers only minor stuff.



          getline()



          Avoid a technical exploit when size == 0. Although this code passes sizes more than 0, the function is hackable with size == 0.



          When sz == 0, as type size_t, sz-1 is a huge value. Simply + 1 on the left-hand side instead.



          // while (i < sz-1 && el == false)
          while (i + 1 < sz && el == false)


          Advanced: getline()



          When a rare reading error occurs, getchar() returns EOF. Standard functions like fgets() return NULL even if some characters were successfully read prior to the error. This differs from OP's getline() functionality. Since getline() uses a return of 0 to indicate end-of-file (and no data read), a parallel functionality to fgets() would also return 0 when an input error occurs (even if some good data read prior).



          Easy, yet pedantic, change suggested:



          if (i < sz) {
          // add if
          if (c == EOF && !feof(stdin)) { // EOF due to error
          i = 0;
          }
          s[i] = '';
          }


          Consider const



          When the source data does not change, using const can make for 1) more clarity in function usage 2) greater applicability as then const char *f; copy(..., f); is possible. 3) potentially more efficient code.



          // void copy(char to, char from);
          void copy(char to, char const from);


          Advanced: Consider restrict



          restrict, roughly, implies that the data referenced by pointer only changes due to the code's function without side effects. Should from/to overlap, copy() as presently coded, can dramatically fail. restrict informs the caller that to/from should not overlap and thus allows the compiler to perform additional optimizations based on that.



          // void copy(char to, char const from);
          void copy(char * restrict to, char const * restrict from);


          Inconsistent documentation/function



          Code is described as "trim trailing tabs and blanks" yet then trims ' ', 't' and 'n'. Recommended consistent documentation and function.



          Sentinels



          When printing string test output, especially ones with white-space removal, use sentinels to help show problems.



          // printf("%s", line);
          printf("<%s>", line);


          bool deserves boolean syntax



          Style issue.



          // while (i < sz-1 && el == false) {
          while (i < sz-1 && !el) {




          No major issues noted. Well done.






          share|improve this answer























          • Thanks! So the EOF test is for when just 1 getchar call returned EOF, but next call would return something else. Currently my program would still read the partial line, and continue reading after the error. With the fix, it would stop at the error, is that right? As for the "trim trailing tabs and blanks", if the string had an n, it puts it back at the end, if the string didn't have it (too long line, or EOF-terminated), it doesn't add it.
            – div0man
            Nov 4 at 10:22












          • @div0man ,"it would stop at the error, is that right? " Yes.
            – chux
            Nov 5 at 6:32










          • Great review; should be pointed out that bool and restrict are a C99 feature and not C90.
            – Neil Edelman
            Nov 7 at 23:09


















          4














          Review covers only minor stuff.



          getline()



          Avoid a technical exploit when size == 0. Although this code passes sizes more than 0, the function is hackable with size == 0.



          When sz == 0, as type size_t, sz-1 is a huge value. Simply + 1 on the left-hand side instead.



          // while (i < sz-1 && el == false)
          while (i + 1 < sz && el == false)


          Advanced: getline()



          When a rare reading error occurs, getchar() returns EOF. Standard functions like fgets() return NULL even if some characters were successfully read prior to the error. This differs from OP's getline() functionality. Since getline() uses a return of 0 to indicate end-of-file (and no data read), a parallel functionality to fgets() would also return 0 when an input error occurs (even if some good data read prior).



          Easy, yet pedantic, change suggested:



          if (i < sz) {
          // add if
          if (c == EOF && !feof(stdin)) { // EOF due to error
          i = 0;
          }
          s[i] = '';
          }


          Consider const



          When the source data does not change, using const can make for 1) more clarity in function usage 2) greater applicability as then const char *f; copy(..., f); is possible. 3) potentially more efficient code.



          // void copy(char to, char from);
          void copy(char to, char const from);


          Advanced: Consider restrict



          restrict, roughly, implies that the data referenced by pointer only changes due to the code's function without side effects. Should from/to overlap, copy() as presently coded, can dramatically fail. restrict informs the caller that to/from should not overlap and thus allows the compiler to perform additional optimizations based on that.



          // void copy(char to, char const from);
          void copy(char * restrict to, char const * restrict from);


          Inconsistent documentation/function



          Code is described as "trim trailing tabs and blanks" yet then trims ' ', 't' and 'n'. Recommended consistent documentation and function.



          Sentinels



          When printing string test output, especially ones with white-space removal, use sentinels to help show problems.



          // printf("%s", line);
          printf("<%s>", line);


          bool deserves boolean syntax



          Style issue.



          // while (i < sz-1 && el == false) {
          while (i < sz-1 && !el) {




          No major issues noted. Well done.






          share|improve this answer























          • Thanks! So the EOF test is for when just 1 getchar call returned EOF, but next call would return something else. Currently my program would still read the partial line, and continue reading after the error. With the fix, it would stop at the error, is that right? As for the "trim trailing tabs and blanks", if the string had an n, it puts it back at the end, if the string didn't have it (too long line, or EOF-terminated), it doesn't add it.
            – div0man
            Nov 4 at 10:22












          • @div0man ,"it would stop at the error, is that right? " Yes.
            – chux
            Nov 5 at 6:32










          • Great review; should be pointed out that bool and restrict are a C99 feature and not C90.
            – Neil Edelman
            Nov 7 at 23:09
















          4












          4








          4






          Review covers only minor stuff.



          getline()



          Avoid a technical exploit when size == 0. Although this code passes sizes more than 0, the function is hackable with size == 0.



          When sz == 0, as type size_t, sz-1 is a huge value. Simply + 1 on the left-hand side instead.



          // while (i < sz-1 && el == false)
          while (i + 1 < sz && el == false)


          Advanced: getline()



          When a rare reading error occurs, getchar() returns EOF. Standard functions like fgets() return NULL even if some characters were successfully read prior to the error. This differs from OP's getline() functionality. Since getline() uses a return of 0 to indicate end-of-file (and no data read), a parallel functionality to fgets() would also return 0 when an input error occurs (even if some good data read prior).



          Easy, yet pedantic, change suggested:



          if (i < sz) {
          // add if
          if (c == EOF && !feof(stdin)) { // EOF due to error
          i = 0;
          }
          s[i] = '';
          }


          Consider const



          When the source data does not change, using const can make for 1) more clarity in function usage 2) greater applicability as then const char *f; copy(..., f); is possible. 3) potentially more efficient code.



          // void copy(char to, char from);
          void copy(char to, char const from);


          Advanced: Consider restrict



          restrict, roughly, implies that the data referenced by pointer only changes due to the code's function without side effects. Should from/to overlap, copy() as presently coded, can dramatically fail. restrict informs the caller that to/from should not overlap and thus allows the compiler to perform additional optimizations based on that.



          // void copy(char to, char const from);
          void copy(char * restrict to, char const * restrict from);


          Inconsistent documentation/function



          Code is described as "trim trailing tabs and blanks" yet then trims ' ', 't' and 'n'. Recommended consistent documentation and function.



          Sentinels



          When printing string test output, especially ones with white-space removal, use sentinels to help show problems.



          // printf("%s", line);
          printf("<%s>", line);


          bool deserves boolean syntax



          Style issue.



          // while (i < sz-1 && el == false) {
          while (i < sz-1 && !el) {




          No major issues noted. Well done.






          share|improve this answer














          Review covers only minor stuff.



          getline()



          Avoid a technical exploit when size == 0. Although this code passes sizes more than 0, the function is hackable with size == 0.



          When sz == 0, as type size_t, sz-1 is a huge value. Simply + 1 on the left-hand side instead.



          // while (i < sz-1 && el == false)
          while (i + 1 < sz && el == false)


          Advanced: getline()



          When a rare reading error occurs, getchar() returns EOF. Standard functions like fgets() return NULL even if some characters were successfully read prior to the error. This differs from OP's getline() functionality. Since getline() uses a return of 0 to indicate end-of-file (and no data read), a parallel functionality to fgets() would also return 0 when an input error occurs (even if some good data read prior).



          Easy, yet pedantic, change suggested:



          if (i < sz) {
          // add if
          if (c == EOF && !feof(stdin)) { // EOF due to error
          i = 0;
          }
          s[i] = '';
          }


          Consider const



          When the source data does not change, using const can make for 1) more clarity in function usage 2) greater applicability as then const char *f; copy(..., f); is possible. 3) potentially more efficient code.



          // void copy(char to, char from);
          void copy(char to, char const from);


          Advanced: Consider restrict



          restrict, roughly, implies that the data referenced by pointer only changes due to the code's function without side effects. Should from/to overlap, copy() as presently coded, can dramatically fail. restrict informs the caller that to/from should not overlap and thus allows the compiler to perform additional optimizations based on that.



          // void copy(char to, char const from);
          void copy(char * restrict to, char const * restrict from);


          Inconsistent documentation/function



          Code is described as "trim trailing tabs and blanks" yet then trims ' ', 't' and 'n'. Recommended consistent documentation and function.



          Sentinels



          When printing string test output, especially ones with white-space removal, use sentinels to help show problems.



          // printf("%s", line);
          printf("<%s>", line);


          bool deserves boolean syntax



          Style issue.



          // while (i < sz-1 && el == false) {
          while (i < sz-1 && !el) {




          No major issues noted. Well done.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 7 at 23:03

























          answered Nov 4 at 3:33









          chux

          12.6k11344




          12.6k11344












          • Thanks! So the EOF test is for when just 1 getchar call returned EOF, but next call would return something else. Currently my program would still read the partial line, and continue reading after the error. With the fix, it would stop at the error, is that right? As for the "trim trailing tabs and blanks", if the string had an n, it puts it back at the end, if the string didn't have it (too long line, or EOF-terminated), it doesn't add it.
            – div0man
            Nov 4 at 10:22












          • @div0man ,"it would stop at the error, is that right? " Yes.
            – chux
            Nov 5 at 6:32










          • Great review; should be pointed out that bool and restrict are a C99 feature and not C90.
            – Neil Edelman
            Nov 7 at 23:09




















          • Thanks! So the EOF test is for when just 1 getchar call returned EOF, but next call would return something else. Currently my program would still read the partial line, and continue reading after the error. With the fix, it would stop at the error, is that right? As for the "trim trailing tabs and blanks", if the string had an n, it puts it back at the end, if the string didn't have it (too long line, or EOF-terminated), it doesn't add it.
            – div0man
            Nov 4 at 10:22












          • @div0man ,"it would stop at the error, is that right? " Yes.
            – chux
            Nov 5 at 6:32










          • Great review; should be pointed out that bool and restrict are a C99 feature and not C90.
            – Neil Edelman
            Nov 7 at 23:09


















          Thanks! So the EOF test is for when just 1 getchar call returned EOF, but next call would return something else. Currently my program would still read the partial line, and continue reading after the error. With the fix, it would stop at the error, is that right? As for the "trim trailing tabs and blanks", if the string had an n, it puts it back at the end, if the string didn't have it (too long line, or EOF-terminated), it doesn't add it.
          – div0man
          Nov 4 at 10:22






          Thanks! So the EOF test is for when just 1 getchar call returned EOF, but next call would return something else. Currently my program would still read the partial line, and continue reading after the error. With the fix, it would stop at the error, is that right? As for the "trim trailing tabs and blanks", if the string had an n, it puts it back at the end, if the string didn't have it (too long line, or EOF-terminated), it doesn't add it.
          – div0man
          Nov 4 at 10:22














          @div0man ,"it would stop at the error, is that right? " Yes.
          – chux
          Nov 5 at 6:32




          @div0man ,"it would stop at the error, is that right? " Yes.
          – chux
          Nov 5 at 6:32












          Great review; should be pointed out that bool and restrict are a C99 feature and not C90.
          – Neil Edelman
          Nov 7 at 23:09






          Great review; should be pointed out that bool and restrict are a C99 feature and not C90.
          – Neil Edelman
          Nov 7 at 23:09




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f206811%2fkr-exercise-1-18-remove-trailing-blanks-and-tabs-from-each-line%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Morgemoulin

          Scott Moir

          Souastre