Reading files using system calls and printing lines - c

This program reads a text file "hello.txt" and looks for the occurrence of the string w in it and prints the number of the line and the whole line. It also prints how many times the string w has occurred in the file. The program compiles with no errors, here is the code:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main() {
int fd;
char c;
char str[152];
int i = 0, j = 0;
int bytesread;
int flag = 1;
int found = 0;
int line = 1;
int foundflag = 1;
char w[] = {'h', 'e', 'l', 'l', 'o'};
int len = strlen(w);
if ((fd = open("hello.txt", O_RDONLY, 0)) != -1) { //if 1
bytesread = read(fd, &c, 1);
str[j] = c;
j++;
if (bytesread != -1) { //if 2
while (bytesread != 0) { //while
if (c == '\n')
line++;
if (c == w[i]) { //if 3
i++;
flag = 0;
} else if (flag == 0 || i == len) //end of f3
{ // else 3
i = 0;
flag = 1;
}// end of else 3
else if (flag == 1) {
while (read(fd, &c, 1)) {
str[j] = c;
j++;
if (c == ' ')
break;
if (c == '\n') {
line++;
break;
}
}
}
bytesread = read(fd, &c, 1);
str[j] = c;
j++;
if ((c == ' ' || c == '\n') && flag == 0 && i == len) {
found++;
foundflag = 0;
printf("w was found in line %d.\n", line);
}
if ((c == '\n')&&(foundflag == 0)) {
for (j = 0; str[j] != '\n'; j += 5) {
printf("%c", str[j]);
if (str[j + 1] != '\n')
printf("%c", str[j + 1]);
else {
j++;
break;
}
if (str[j + 2] != '\n')
printf("%c", str[j + 2]);
else {
j += 2;
break;
}
if (str[j + 3] != '\n')
printf("%c", str[j + 3]);
else {
j += 3;
break;
}
if (str[j + 4] != '\n')
printf("%c", str[j + 4]);
else {
j += 4;
break;
}
}
for (; str[j] != '\n'; j++)
printf("%c", str[j]);
printf("\n");
j = 0;
} else if (c == '\n')
foundflag = 1;
} //end of while
printf("w has occured %d times.\n", found);
} else //end of if 2
printf("couldn't read file.\n");
} else //end of if 1
printf("Couldn't open file for read.\n");
close(fd);
} //end of main
This is the output in the terminal:
w was found in line 1.
hello
w was found in line 2.
w was found in line 6.
hello world
hellooooo
w has occured 3 times.
and here is the content of "hello.txt":
hello
hello world
hallo
I'm here
we're here
hello
hellooooo
the number of lines printed in the output are 1,2 & 6, but this is what the output should look like:
w was found in line 1.
hello
w was found in line 2.
hello world
w was found in line 6.
hello
w has occured 3 times.

I suggest you read some C material. You code suggests you don't know much about the language yet.
I won't change your code, since that'd be hard.
I'll post the relevant parts of my code and explain the bits.
So, the code bits:
const char fname[] = "hello.txt";
const char w[] = "hello";
(...)
while (read(fd, &buffer[i], 1) == 1) {
/* end of line */
if (buffer[i] == '\n' || buffer[i] == 0x0) {
buffer[i] = 0;
if (!strncmp(buffer, w, strlen(w))) {
printf("w was found in line %d\n", line);
puts(buffer);
n++;
}
line++;
i = 0;
continue;
}
i++;
}
Explanation
while (read(fd, &buffer[i], 1) == 1):
This will read one character from your fd (which was returned by an earlier open call) and store that at buffer[i]. The relevant thing to note here is that before this you should have declared int i = 0 and make sure buffer is either a defined array or a malloced memory region. This while will go on until the amount of bytes read is different than 1 (which was what we asked for).
if (buffer[i] == '\n' || buffer[i] == 0x0): This if detects an end of line. Pretty straight-forward.
buffer[i] = 0; and if (!strncmp(buffer, w, strlen(w))): buffer[i] = 0 will set the last character of the current buffer to zero. What this does is it gets rid of the last \n we read, so we can print it nicely with puts. The bit I suggested in the comments is the use of strncmp. This function is just like strcmp, but it will only compare at most a defined number of bytes. So, with this function, you can effectively determine if a string starts with the substring you're looking for. If this string is found, we print the line in which it is, print the buffer itself and increment n, which is our counter on how many times w was found. You should have declared int n = 0; in the beginning of the code...
line++; i = 0; continue; :This is inside the end-of-line detection if. So, what this does is it increments our line counter, sets i to zero - this is important, because at a new line we're gonna read a new buffer, and that buffer index must start at 0. And continue forces a loop repetition without executing the rest of the code.
Finally, the rest of the while scope is defined as i++. As our while loop executes at every character, the buffer index must get incremented after every character read.
The file I tested with is the one you provided. The output I got was:
w was found in line 1
hello
w was found in line 2
hello world
w was found in line 6
hello
w was found 3 times

Related

Counting number of words inside a text file and printing results in a different text file in c/c++

the code:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
char filename[] = "11.txt";
char filename1[] = "2.txt";
FILE *ptr, *resultptr;
char string[100];
char words[100][100];
int len = sizeof(filename) / sizeof(char);
int i = 0, j = 0, k, length, count;
int main()
{
fopen_s(&ptr, filename, "r");
fopen_s(&resultptr, filename1, "w");
if ((ptr == nullptr) || (resultptr == nullptr)) {
printf("Files were not opened!");
return -1;
}
while (fgets(string, sizeof string, ptr)) {
for (k = 0; string[k] != '\0'; k++) {
if (string[k] != ' ' && string[k] != '\n') {
words[i][j++] = tolower(string[k]);
} else {
words[i][j] = '\0';
i++;
j = 0;
}
}
length = i + !!j;
fputs("Occurrences of each word:\n", resultptr); //prints this sentence into file
for (i = 0; i < length; i++) {
if (strcmp(words[i], "0") == 0)
continue;
count = 1;
char *ch = words[i];
for (j = i + 1; j < length; j++) {
if (strcmp(words[i], words[j]) == 0 && (strcmp(words[j], "0") != 0)) {
count++;
strcpy_s(words[j], "0");
}
}
fputs("The word ", resultptr);
if (string[i] != ' ' && string[i] != '\n') {
fprintf(resultptr, "%s", ch);
}
fputs(" occurred ", resultptr);
fprintf(resultptr, "%d", count);
fputs(" times\n", resultptr);
}
fclose(ptr);
fclose(resultptr);
return 0;
}
}
The counting part is working perfectly fine, but the problem is when I try to print results, for the sentence "to be or not: to be that is the question ..." it prints this:
Occurrences of each word:
The word to occurred 2 times
The word be occurred 2 times
The word occurred 1 times
The word not: occurred 1 times
The word that occurred 1 times
The word is occurred 1 times
The word occurred 1 times
Occurrences of each word:
The word to occurred 1 times
The word be occurred 1 times
The word or occurred 1 times
The word occurred 1 times
The word that occurred 1 times
The word is occurred 1 times
The word occurred 2 times
The word question occurred 1 times
The word ... occurred 1 times
What's messing? like I'm not professional, but can someone guide me on what's wrong here? I changed a bit from the original one but still a lot of mistakes
There are multiple problems in the code:
the global variables should be moved inside the body of the main() function.
fopen_s() is not portable, use fopen() instead.
strcpy_s() is not portable, use strcpy() instead or just set the first byte if the string to '\0' to make it an empty string.
i and j should be reset to 0 after each fgets().
you should test for letters with isalpha() instead of only testing for space and newline.
you should clear the duplicated words by setting them to the empty string.
you should use a simple fprintf() call for the output line.
you should not close the files inside the while(fgets(...)) loop.
If you want to count all words in the file, this approach is limited to a rather small number of words. A more general solution would construct a dictionary of words found as you read the file contents and increment the count for each word found.
Here is a modified version:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#ifdef _MSC_VER
#pragma warning(disable:4996) // disable Microsoft obnoxious warning
#endif
#define WORDS 2000
#define CHARS 40
int main() {
char filename[] = "11.txt";
char filename1[] = "2.txt";
FILE *ptr, *resultptr;
char string[100];
char words[WORDS][CHARS];
int i, j, k, length, count;
ptr = fopen(filename, "r");
if (ptr == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
return 1;
}
resultptr = fopen(filename1, "w");
if (resultptr == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename1, strerror(errno));
return 1;
}
i = j = 0;
while (i < WORDS && fgets(string, sizeof string, ptr)) {
for (k = 0; string[k] != '\0'; k++) {
unsigned char c = string[k];
if (isalpha(c)) {
if (j < CHARS - 1)
words[i][j++] = tolower(c);
} else {
words[i][j] = '\0';
if (j > 0) {
j = 0;
i++;
if (i == WORDS)
break;
}
}
}
if (j > 0) {
// include the last word if the file does not end with a newline
words[i][j] = '\0';
i++;
}
}
length = i;
fprintf(resultptr, "Occurrences of each word:\n");
for (i = 0; i < length; i++) {
if (words[i][0] == '\0')
continue;
count = 1;
for (j = i + 1; j < length; j++) {
if (strcmp(words[i], words[j]) == 0) {
count++;
words[j][0] = '\0';
}
}
fprintf(resultptr, "The word %s occurred %d times\n", words[i], count);
}
fclose(ptr);
fclose(resultptr);
return 0;
}
Note: Meanwhile, OP has applied the mentioned fixes to the question, thereby invalidating this answer. This answer applies to revision 4 of the question.
What's messing?
The word 0 occurred 1 times - You chose to replace word duplicates with the string "0". In order to not count those replacements as words, insert
if (strcmp(words[i], "0") == 0) continue;
at the very beginning of the printing for loop body. It seems you intended if (string[i] != ' ' && string[i] != '\0' && string[i]!='0' ) to do this, but that doesn't work - remove this code.
Besides, the empty string would be a better choice, allowing the word 0.
The word
occurred 1 times - The '\n' at the end was counted as a word. In order to not count this and in addition skip punctuation as well as avoid empty words due to consecutive non-word characters, replace
if (string[k] != ' ' && string[k] != '\0') {
words[i][j++] = tolower(string[k]);
}
else
with
if (isalnum(string[k]))
words[i][j++] = tolower(string[k]);
else if (j)
The word occurred 1 times - An empty word at the end of file was counted. In order to not count that, add 1 to i only if inside a word at EOF, i. e. change
length = i + 1;
to
length = i + !!j;

How to print a string based on the postion in c?

I am working on a question which requires me to print a string given a field-number at that position. The strings should be read from a file.
file.txt
C is a language.
lex lexical analyser
(blank line)
gcc is good
If the field-number is 2 (i.e the second word in the sentence). The program should output
is
lexical
(NULL)
is
I wrote a function but don't think its the correct way and that it would work for all cases. It should handle extra blanks or newlines.
while (fgets(buffer, MAX, file) != NULL) {
for (int i = 1; i < strlen(buffer); i++) {
if (count == field_number - 1) {
int j = i;
while (j < strlen(buffer) && buffer[j] != ' ') {
printf("%c", buffer[j++]);
}
printf("\n");
count = 0;
break;
}
if (buffer[i] == ' ' && buffer[i - 1] != ' ') {
count++;
}
}
}
I am a beginner. This code should be easy to understand.
This should work for all the cases,
int main() {
//FILE* file = fopen(__FILE__, "r");
//int field_number = 2;
int new_line = 0; // var to keep track of new line came or not
int word = 0;
int count = 0;
char c, prev_c;
while ((c = fgetc(file)) != EOF) {
// printf("[%c]", c);
// if a new line char comes it means you entered a new line
if(c == '\n') {
// you have to print the new line here on the output to handle
// empty line cases
printf("\n");
new_line = 1; // when line changes
word = 0; // no word has come in this new line so far
count = 0; // count becomes 0
} else if( c == ' ' && prev_c != ' ') {
if(word)
count++;
if(count == field_number) // if count exceeds field_number
new_line = 0; // wait till next line comes
} else if (new_line && count == field_number - 1) {
printf("%c", c);
} else {
word = 1; // fi a not new line or non space char comes, a word has come
}
prev_c = c;
}
return 0;
}

Folding input lines every nth column (K&R 1-22) in C

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.
The algorithm I decided to follow for this was as follows:
If length of input line < maxcol (the column after which one would have to fold), then print the line as it is.
If not, from maxcol, I check towards it's left, and it's right to find the closest non-space character, and save them as 'first' and 'last'. I then print the character array from line[0] to line[first] and then the rest of the array, from line[last] to line[len] becomes the new line array.
Here's my code:
#include <stdio.h>
#define MAXCOL 5
int getline1(char line[]);
int main()
{
char line[1000];
int len, i, j, first, last;
len = getline1(line);
while (len > 0) {
if (len < MAXCOL) {
printf("%s\n", line);
break;
}
else {
for (i = MAXCOL - 1; i >= 0; i--) {
if (line[i] != ' ') {
first = i;
break;
}
}
for (j = MAXCOL - 1; j <= len; j++) {
if (line[j] != ' ') {
last = j;
break;
}
}
//printf("first %d last %d\n", first, last);
for (i = 0; i <= first; i++)
putchar(line[i]);
putchar('\n');
for (i = 0; i < len - last; i++) {
line[i] = line[last + i];
}
len -= last;
first = last = 0;
}
}
return 0;
}
int getline1(char line[])
{
int c, i = 0;
while ((c = getchar()) != EOF && c != '\n')
line[i++] = c;
if (c == '\n')
line[i++] = '\n';
line[i] = '\0';
return i;
}
Here are the problems:
It does not do something intelligent with very long lines (this is fine, as I can add it as an edge case).
It does not do anything for tabs.
I cannot understand a part of the output.
For example, with the input:
asd de def deffff
I get the output:
asd
de
def
defff //Expected until here
//Unexpected lines below
ff
fff
deffff
deffff
deffff
Question 1 - Why do the unexpected lines print? How do I make my program/algorithm better?
Eventually, after spending quite some time with this question, I gave up and decided to check the clc-wiki for solutions. Every program here did NOT work, save one (The others didn't work because they did not cover certain edge cases). The one that worked was the largest one, and it did not make any sense to me. It did not have any comments, and neither could I properly understand the variable names, and what they represented. But it was the ONLY program in the wiki that worked.
#include <stdio.h>
#define YES 1
#define NO 0
int main(void)
{
int TCOL = 8, ch, co[3], i, COL = 19, tabs[COL - 1];
char bls[COL - 1], bonly = YES;
co[0] = co[1] = co[2] = 0;
while ((ch = getchar()) != EOF)
{
if (ch != '\t') {
++co[0];
++co[2];
}
else {
co[0] = co[0] + (TCOL * (1 + (co[2] / TCOL)) - co[2]);
i = co[2];
co[2] = TCOL + (co[2] / TCOL) * TCOL;
}
if (ch != '\n' && ch != ' ' && ch != '\t')
{
if (co[0] >= COL) {
putchar('\n');
co[0] = 1;
co[1] = 0;
}
else
for (i = co[1]; co[1] > 0; --co[1])
{
if (bls[i - co[1]] == ' ')
putchar(bls[i - co[1]]);
else
for (; tabs[i - co[1]] != 0;)
if (tabs[i - co[1]] > 0) {
putchar(' ');
--tabs[i - co[1]];
}
else {
tabs[i - co[1]] = 0;
putchar(bls[i - co[1]]);
}
}
putchar(ch);
if (bonly == YES)
bonly = NO;
}
else if (ch != '\n')
{
if (co[0] >= COL)
{
if (bonly == NO) {
putchar('\n');
bonly = YES;
}
co[0] = co[1] = 0;
}
else if (bonly == NO) {
bls[co[1]] = ch;
if (ch == '\t') {
if (TCOL * (1 + ((co[0] - (co[2] - i)) / TCOL)) -
(co[0] - (co[2] - i)) == co[2] - i)
tabs[co[1]] = -1;
else
tabs[co[1]] = co[2] - i;
}
++co[1];
}
else
co[0] = co[1] = 0;
}
else {
putchar(ch);
if (bonly == NO)
bonly = YES;
co[0] = co[1] = co[2] = 0;
}
}
return 0;
}
Question 2 - Can you help me make sense of this code and how it works?
It fixes all the problems with my solution, and also works by reading character to character, and therefore seems more efficient.
Question 1 - Why do the unexpected lines print? How do I make my program/algorithm better?
You are getting the unexpected lines in the output because after printing the array, you are not terminating the new line array with null character \0 -
Here you are copying character from starting from last till len - last, creating a new line array:
for (i = 0; i < len - last; i++) {
line[i] = line[last + i];
}
You have copied the characters but the null terminating character is still at its original position. Assume the input string is:
asd de def deffff
So, initially the content of line array will be:
"asd de def deffff\n"
^
|
null character is here
Now after printing asd, you are copying characters from last index of line till len - last index to line array itself starting from 0 index. So, after copying the content of line array will be:
"de def deffff\n deffff\n"
|____ _____|
\/
This is causing the unexpected output
(null character is still at the previous location)
So, after for loop you should add the null character just after the last character copied, like this:
line [len - last] = '\0';
With this the content of line array that will be processed in the next iteration of while loop will be:
"de def deffff\n"
One more thing, in the line array you can see the \n (newline) character at the end. May you want to remove it before processing the input, you can do:
line[strcspn(line, "\n")] = 0;
Improvements that you can do in your program:
1. One very obvious improvement that you can do is to use pointer to the input string while processing it. With the help of pointer you don't need to copy the rest of the array, apart from processed part, again to the same array till the program process the whole input. Initialize the pointer to the start of the input string and in every iteration just move the pointer to appropriate location and start processing from that location where pointer is pointing to.
2. Since you are taking the whole input first in a buffer and then processing it. You may consider fgets() for taking input. It will give better control over the input from user.
3. Add a check for line array overflow, in case of very long input. With fgets() you can specify the maximum number of character to be copied to line array from input stream.
Question 2 - Can you help me make sense of this code and how it works?
The program is very simple, try to understand it at least once by yourself. Either use a debugger or take a pen and paper, dry run it once for small size input and check the output. Increase the input size and add some variations like multiple space characters and check the program code path and output. This way you can understand it very easily.
Here's another (and I think better) solution to this exercise :
#include <stdio.h>
#define MAXCOL 10
void my_flush(char buf[]);
int main()
{
int c, prev_char, i, j, ctr, spaceleft, first_non_space_buf;
char buf[MAXCOL+2];
prev_char = -1;
i = first_non_space_buf = ctr = 0;
spaceleft = MAXCOL;
printf("Just keep typing once the output has been printed");
while ((c = getchar()) != EOF) {
if (buf[0] == '\n') {
i = 0;
my_flush(buf);
}
//printf("Prev char = %c and Current char = %c and i = %d and fnsb = %d and spaceleft = %d and j = %d and buf = %s \n", prev_char, c, i, first_non_space_buf, spaceleft, j, buf);
if ((((prev_char != ' ') && (prev_char != '\t') && (prev_char != '\n')) &&
((c == ' ') || (c == '\t') || (c == '\n'))) ||
(i == MAXCOL)) {
if (i <= spaceleft) {
printf("%s", buf);
spaceleft -= i;
}
else {
putchar('\n');
spaceleft = MAXCOL;
for (j = first_non_space_buf; buf[j] != '\0'; ++j) {
putchar(buf[j]);
++ctr;
}
spaceleft -= ctr;
}
i = 0;
my_flush(buf);
buf[i++] = c;
first_non_space_buf = j = ctr = 0;
}
else {
if (((prev_char == ' ') || (prev_char == '\t') || (prev_char == '\n')) &&
((c != ' ') && (c != '\t') && (c != '\n'))) {
first_non_space_buf = i;
}
buf[i++] = c;
buf[i] = '\0';
}
prev_char = c;
}
printf("%s", buf);
return 0;
}
void my_flush(char buf[])
{
int i;
for (i = 0; i < MAXCOL; ++i)
buf[i] = '\0';
}
Below is my solution, I know the thread is no longer active but my code might help someone who's facing issues to grasp the already presented code snippets.
*EDIT
explaination
Keep reading input unless the input contains '\n', '\t' or there've been
atleast MAXCOl chars.
Incase of '\t', use expandTab to replace with required spaces and use printLine if it doesn't exceed MAXCOl.
Incase of '\n', directly use printLine and reset the index.
If index is 10:
find the last blank using findBlank ad get a new index.
use printLine to print the current line.
get new index as 0 or index of newly copied char array using the newIndex function.
code
/* fold long lines after last non-blank char */
#include <stdio.h>
#define MAXCOL 10 /* maximum column of input */
#define TABSIZE 8 /* tab size */
char line[MAXCOL]; /* input line */
int expandTab(int index);
int findBlank(int index);
int newIndex(int index);
void printLine(int index);
void main() {
int c, index;
index = 0;
while((c = getchar()) != EOF) {
line[index] = c; /* store current char */
if (c == '\t')
index = expandTab(index);
else if (c == '\n') {
printLine(index); /* print current input line */
index = 0;
} else if (++index == MAXCOL) {
index = findBlank(index);
printLine(index);
index = newIndex(index);
}
}
}
/* expand tab into blanks */
int expandTab(int index) {
line[index] = ' '; /* tab is atleast one blank */
for (++index; index < MAXCOL && index % TABSIZE != 0; ++index)
line[index] = ' ';
if (index > MAXCOL)
return index;
else {
printLine(index);
return 0;
}
}
/* find last blank position */
int findBlank(int index) {
while( index > 0 && line[index] != ' ')
--index;
if (index == 0)
return MAXCOL;
else
return index - 1;
}
/* re-arrange line with new position */
int newIndex(int index) {
int i, j;
if (index <= 0 || index >= MAXCOL)
return 0;
else {
i = 0;
for (j = index; j < MAXCOL; ++j) {
line[i] = line[j];
++i;
}
return i;
}
}
/* print line until passed index */
void printLine(int index) {
int i;
for(i = 0; i < index; ++i)
putchar(line[i]);
if (index > 0)
putchar('\n');
}

Getc() reading \n improperly

I am creating a program that prints each line of a file one by one in reverse word order. i.e. "The big black moose" prints as "moose black big The".
However, in the code below, it does not print the last word of lines that do not have a delimiter before the line break. A delimiter in this case is defined as any whitespace character such as space or tab.
int main(int argc, char const *argv[]) {
if (argc != 2) return 0;
int i = 0, c;
int isD = 0, wasD = 1;
int count = 0;
FILE *file = fopen(argv[1], "r");
while ((c = getc(file)) != EOF) {
isD = c == ' ' || c == '\t' || c == '\n';
if (!isD) {
chars[i++] = c;
count++;
}
if (isD && !wasD) {
shiftInsert(i++, count);
count = 0;
}
wasD = isD;
}
fclose(file);
return 0;
}
int shiftInsert(int length, int shift) {
int word[shift+1], i;
printf("\n----------\nL:%d,S:%d\n", length, shift);
for (i = 0; i < shift; i++)
word[i] = chars[length-shift+i];
word[shift] = ' ';
for (i = 0; i < shift; i++)
printf("%c", word[i]);
for (i = length; i >= 0; i--)
chars[i+shift+1] = chars[i];
for (i = 0; i <= shift; i++)
chars[i] = word[i];
printf("|");
}
This happens, because you don't enter the loop when getc finds the end of the file. If wasD is false, you'll have one unprocessed word in the buffer.
You could treat EOF as whitespace and place the terminating condition at the end of the loop:
do {
c = getc(file);
isD = (c == ' ' || c == '\t' || c == '\n' || c == EOF);
// ...
} while (c != EOF);
This works, because you use the value of c only if it is not a delimiter. (The special value EOF is outside the valid range of (unsigned) chars and should not be inserted into strings or printed.)
stdout is not getting flushed because your last output didn't contain a newline...
Change this line
printf("|");
to
printf("|\n");

C for loop to iterate array different from putc

I'm trying to read from a text file and for non printing ascii characters I would like to print out "^" + "G" as an example of the BELL character. Much like the cat -v command of unix. The problem happens in the for loop where I am supposed to store chars until I hit a newline and then print them out. The for loop is printing "G " for ctrl+G and "t " "e " "s " "t " for test.
int readFile(FILE* inputFile) {
char input[5];
char *arrayEnd = &input[5]+1;
int anyChanges = 1;
int iochar = 0;
int i = 0;
//get index of new line
//substring of position until new line
//print substring position to end.
int printedColumns = 0;
//credit Foster Chapter 2
while (( iochar = getc(inputFile) ) != EOF )
{ //Returns 1 if no changes made, return 0 if any changes have been made.
//printf("character --> %c\n",iochar);
if(iochar != '\n') {
//This if statement checks for normal ascii characters.
//If the output is less than 72 it prints it and increments printedColumns.
if (( ' ' <= iochar ) && ( iochar <= 126 ) ) {
if(*(input + i) == *arrayEnd)
{
i = 0;
}
*(input +i) = iochar;
//printf("input array ---> %c\n",input[i]);
//printf("i:%d\n",i);
//printf("iochar:%d\n",iochar);
//putc(*(input+i), stdout);
i++;
}
//This if statement checks for the non-printing characters.
//New line is not included because it is a special case that is accounted for below
if (iochar <= 31) {
if (*(input + i) == *arrayEnd)
{
i = 0;
}
*(input + i) =94;
putc(*(input+i), stdout);
i++;
if(*(input+i)== *arrayEnd)
{
i = 0;
}
*(input + i) = iochar + 64;
putc(*(input+i), stdout);
printf("\n");
i++;
}
int b = 0;
for (b = 0;b<6;b++){
putc(*(input+b),stdout);
}
}//end if != '\n'
}//end while
return anyChanges;
}//end function
This seems to do a lot of what you're looking for:
#include <stdio.h>
static
int readFile(FILE *inputFile)
{
int numChanged = 0;
int iochar = 0;
while ((iochar = getc(inputFile) ) != EOF)
{
if ((' ' <= iochar && iochar <= 126) || iochar == '\n')
putc(iochar, stdout);
else if (iochar < ' ')
{
putc('^', stdout);
putc(iochar + 'A' - 1, stdout);
numChanged++;
}
else
numChanged++;
}
return numChanged;
}
int main(void)
{
printf("Number of changed characters: %d\n", readFile(stdin));
return 0;
}
It worked correctly on its own source code (no characters to change — no tabs in the source), and it seemed to work correctly on its own binary too. Neither output is exciting enough to quote here. Note that it deletes characters from code 0x7F through 0xFF. You can amend that by adjusting the else clause appropriately for whatever rules you choose to specify. The question was silent on the treatment of such characters.
If you need the last 72 characters of a line, then you need to read whole lines; roll in fgets():
#include <stdio.h>
#include <string.h>
static
int readFile(FILE *fp)
{
int numChanged = 0;
char line[4096];
while (fgets(line, sizeof(line), fp) != 0)
{
size_t len = strlen(line);
if (line[len-1] != '\n')
break; // Damn! Line to long
size_t start = 0;
if (len > 72)
start = len - 72;
for (size_t i = start; i < len; i++)
{
/* The next line is only 70 characters long, but this comment should be truncated */
if ((' ' <= line[i] && line[i] <= 126) || line[i] == '\n')
putc(line[i], stdout);
else if (line[i] < ' ')
{
putc('^', stdout);
putc(line[i] + 'A' - 1, stdout);
numChanged++;
}
else
numChanged++;
}
}
return numChanged;
}
int main(void)
{
printf("Number of changed characters: %d\n", readFile(stdin));
return 0;
}
This code, run on its own, carefully kludged source, yields the interesting part:
start = len - 72;
for (size_t i = start; i < len; i++)
{
ine is only 70 characters long, but this comment should be truncated */
if ((' ' <= line[i] && line[i] <= 126) || line[i] == '\n')
putc(line[i], stdout);
else if (line[i] < ' ')
{
You can decide if I've got an off-by-one error in counting the lengths.

Resources