{"id":3929,"date":"2020-01-08T00:01:32","date_gmt":"2020-01-08T08:01:32","guid":{"rendered":"https:\/\/c-for-dummies.com\/blog\/?p=3929"},"modified":"2020-01-04T09:27:35","modified_gmt":"2020-01-04T17:27:35","slug":"remove-trailing-blank-lines-solution","status":"publish","type":"post","link":"https:\/\/c-for-dummies.com\/blog\/?p=3929","title":{"rendered":"Remove Trailing Blank Lines &#8211; Solution"},"content":{"rendered":"<p>The problem with snipping blank lines from the end of a file is storing the file as it&#8217;s processed. At least that&#8217;s the issue I faced as I worked through my solution to <a href=\"https:\/\/c-for-dummies.com\/blog\/?p=3914\">this month&#8217;s Exercise<\/a>.<br \/>\n<!--more--><br \/>\nMy first approach was to write a filter to solve the problem. The filter processes one character at a time &mdash; or even a line or two at a time &mdash; looking for the EOF and then working backwards to eliminate all but the final newline. The problem with this approach is that you could theoretically encounter a file that has a ton of newlines, more than would fit into the buffer.<\/p>\n<p>Another thought was to move the file position indicator to the end of the file and scan backwards for excess newlines. The location of the final, necessary newline could be located, its position marked, then the rest of the file output up until this point. The issue I faced here is that, again, a file may have dozens of newlines or even be composed solely of newlines. And working backwards through a file to find a given offset requires more overhead than I was willing to code.<\/p>\n<p>The solution I chose is to store the entire file in a dynamic buffer. As each chunk of text is read from the file, the buffer is reallocated, the new text chunk is then appended to the buffer&#8217;s tail.<\/p>\n<p>The <em>malloc()<\/em> function allocates and initializes the buffer, <code>b<\/code>, before any data is read from the file:<\/p>\n<pre class=\"screen\">\r\n    <span class=\"comments\">\/* allocate and initialize the big buffer *\/<\/span>\r\n    b = (char *)malloc( sizeof(char)*1 );\r\n    if( b==NULL )\r\n    {\r\n        fclose(fp);\r\n        fprintf(stderr,\"Memory allocation error\\n\");\r\n        exit(1);\r\n    }\r\n    *b = '\\0';\r\n    length = 0;<\/pre>\n<p>If memory isn&#8217;t available, the file pointer <code>fp<\/code> is closed and the program exits. Otherwise, a one-byte buffer is initialized with a null character, <code>*b = '\\0';<\/code> an its length set to zero.<\/p>\n<p>The next step is to read 64-character chunks from the file and reallocate the buffer to accommodate. Defined constant <code>SIZE<\/code> holds the value 64:<\/p>\n<pre class=\"screen\">\r\n    <span class=\"comments\">\/* read the file *\/<\/span>\r\n    while( !feof(fp) )\r\n    {\r\n        <span class=\"comments\">\/* read SIZE bytes of data *\/<\/span>\r\n        r = fgets(buffer,SIZE,fp);\r\n        <span class=\"comments\">\/* confirm data read *\/<\/span>\r\n        if( r==NULL )\r\n            break;\r\n        <span class=\"comments\">\/* reallocate buffer *\/<\/span>\r\n        length = strlen(buffer) + strlen(b);\r\n        b = (char *)realloc( b, length+1 );\r\n        if( b==NULL )\r\n        {\r\n            fclose(fp);\r\n            fprintf(stderr,\"Memory allocation error\\n\");\r\n            exit(1);\r\n        }\r\n        strcat(b,buffer);\r\n    }<\/pre>\n<p>The <em>fgets()<\/em> function reads from the file, and its return value is tested to confirm data was read.<\/p>\n<p>Variable <code>length<\/code> is recalculated to determine the tally of characters read from a file. Its value is used in the <em>realloc()<\/em> function to re-size the buffer, with one byte added for the null character at the end of a string.<\/p>\n<p>Finally, the <em>strcat()<\/em> function appends the text read from the file into the dynamic buffer, <code>b<\/code>.<\/p>\n<p>After the file is read into the buffer, another <em>while<\/em> loop locates the first of any number of trailing newline characters and caps the buffer immediately after:<\/p>\n<pre class=\"screen\">\r\n    <span class=\"comments\">\/* The file's text is now in the buffer.\r\n       Process the text from the end back *\/<\/span>\r\n        <span class=\"comments\">\/* decrement the length because the\r\n           first byte is zero *\/<\/span>\r\n    length--;\r\n        <span class=\"comments\">\/* search for the first non-newline *\/<\/span>\r\n    while( *(b+length)=='\\n' )\r\n    {\r\n        length--;\r\n    }\r\n        <span class=\"comments\">\/* preserve the last newline *\/<\/span>\r\n    length+=2;\r\n        <span class=\"comments\">\/* cap the string *\/<\/span>\r\n    *(b+length) = '\\0';<\/pre>\n<p>The buffer need not be resized at this point; a <em>printf()<\/em> statement dumps its contents and the program is done.<\/p>\n<p><a href=\"https:\/\/github.com\/dangookin\/C-For-Dummies-Blog\/blob\/master\/2020_01-Exercise.c\" rel=\"noopener noreferrer\" target=\"_blank\">Click here<\/a> to view the full source code on GitHub.<\/p>\n<p>I hope you devised a clever solution. The proof is running test files through the program to ensure that all trailing newlines are removed and the remainder of the file is intact.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The problem with snipping blank lines from the end of a file is storing the file as it&#8217;s processed. At least that&#8217;s the issue I faced as I worked through my solution to this month&#8217;s Exercise.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-3929","post","type-post","status-publish","format-standard","hentry","category-solution"],"_links":{"self":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/3929","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3929"}],"version-history":[{"count":3,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/3929\/revisions"}],"predecessor-version":[{"id":3938,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/3929\/revisions\/3938"}],"wp:attachment":[{"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3929"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3929"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/c-for-dummies.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3929"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}