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");
Related
A palindrome is a word that reads the same from left to right and from right to left.
I wrote a program that finds palindromes from a console.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define SIZE 100
int main() {
int i = 0, c;
int left, right;
char string[SIZE];
while (EOF != (c = getchar()) || (c = getchar()) != '\n') {
if (isspace(c) != 0) {
if (i > 0) {
left = 0;
right = i - 1;
while (right > left) {
if (string[left] != string[right]) {
i = 0;
break;
}
++left;
--right;
}
if (left >= right) {
while (i > 0)
printf("%c", string[--i]);
printf("%c", c);
}
i = 0;
}
if (c == '\n')
break;
}
else {
string[i++] = c;
}
}
}
For example, we enter the words: dad sad mum. She outputs: dad mum. But if we write dad sad or dad mum sad. The output will be: dad mum.
That is, an extra space is printed when the last word we read is not a palindrome. How can you get around this situation?
Code is convoluted
First read input properly and form a string.
for (i = 0; i < SIZE - 1; i++) [
int ch = getchar();
if (ch == EOF || ch == '\n') {
break;
}
string[i++] = (char) ch;
}
string[i] = 0;
Then process the string in string[]. Only print spaces when needed.
const char *separator = "";
int i = 0;
while (string[i]) {
// Beginning of a word?
if (!isspace(string[i])) {
int start = i;
int end = i;
while (!isspace(string[end+1]) && string[end+1]) {
end++;
}
// At this point, start indexes the 1st char of the word
// and end indexes the last char of the word
// Now find if a palindrome
while (start <= end && string[start] == string[end]) {
start++;
end--;
}
// Found a palindrome?
if (start > end) {
fputs(separator, stdout);
separator = " "; // print a space _next_ time
while (!isspace(string[i]) && string[i]) {
fputc(string[i++], stdout);
}
} else {
i = end + 1;
}
} else {
i++;
}
}
fputc('\n', stdout);
Life is easier if you just read the string all at once, then process the string.
char s[1000];
fgets( s, sizeof(s), stdin );
char * p = strchr( s, '\n' );
if (p) *p = '\0';
If you wanted to read one character at a time you should read once, test twice:
int c;
while ( ((c = getchar()) != '\n') and (c != EOF) )
But trying to compute the palindrome-ness at the same time as reading seriously makes your algorithm waaaaay more complicated than it needs to be. Read a string first, then compute.
Now you can use integer indices from each end of the string. If you can get them to meet (or cross) then you’ve got a palindrome. Hint: put that in a function:
bool is_palindrome( const char * s )
{
int left = 0;
int right = strlen(s) - 1;
...
}
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;
}
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');
}
So my program should get input from an user and store it in an array. After that if the input string includes three 'a's in a row it should be replaced with a single '*'. However I can't seem to get it right. It only replaces the first a with a *. I tried to replace the following 2 a with a blank but the output looks funny.
For this exercise I have to use putchar() and getchar().
Thank you in advance.
#include <stdio.h>
char c;
char buffer[256];
int counter= 0;
int i;
int main()
{
while ((c = getchar()) != '\n') {
buffer[counter] =c;
counter++;
if (counter >255) {
break;
}
}
for(i=0; i<256; i++) {
if(buffer[i]== 'a'&&buffer[i+1]=='a'&&buffer[i+2]=='a')
{
buffer[i]= '*';
buffer[i+1]=' ';
buffer[i+2]=' ';
}
putchar(buffer[i]);
}
putchar('\n');
return 0;
}
So my program should get input from an user and store it in an array.
After that if the input string includes three 'a's in a row it should
be replaced with a single '*'. However I can't seem to get it right.
You almost got it! Just move index by 2 to and continue.
#include <stdio.h>
char c;
char buffer[256];
int counter= 0;
int i;
int main(void)
{
while ((c = getchar()) != '\n') {
buffer[counter] =c;
counter++;
if (counter >= 255) {
break;
}
}
buffer[counter] ='\0';
for(i=0; i<256; i++) {
if(buffer[i]== 'a'&&buffer[i+1]=='a'&&buffer[i+2]=='a')
{
buffer[i]= '*';
putchar(buffer[i]);
i = i + 2;
continue;
}
putchar(buffer[i]);
}
putchar('\n');
return 0;
}
Test:
123aaa456aaa78
123*456*78
In string you must assign a end of character at the end and that is call null character \0 or just a numeric 0. Correct your code like below:-
while ((c = getchar()) != '\n') {
buffer[counter] =c;
counter++;
if (counter >=255) {
break;
}
}
buffer[counter] ='\0';// or buffer[counter] =0;
To avoid side effect in a string array always set all its value with 0 first:-
char buffer[256];
memset(buffer, 0, sizeof(buffer));
If you want to change the number of characters, you will need to create a different buffer to copy the output to.
If you really just want to output to the console, you could just write every character until you hit your matching string.
#include <stdio.h>
char c;
char buffer[256];
char output[256];
int counter= 0;
int i, j;
int main()
{
while ((c = getchar()) != '\n') {
buffer[counter] = c;
counter++;
if (counter >255) {
break;
}
}
buffer[counter] = 0;
for(i=0, j=0; i<256; i++, j++) {
if(buffer[i] == 'a' && buffer[i+1] == 'a'&& buffer[i+2] == 'a')
{
output[j]= '*';
i += 2;
}
else
output[j] = buffer[i];
putchar(output[j]);
}
putchar('\n');
return 0;
}
There are multiple problems in your code:
there is no reason to make all these variables global. Declare them locally in the body of the main function.
use int for the type of c as the return value of getchar() does not fit in a char.
you do not check for EOF.
your test for buffer overflow is off by one.
you do not null terminate the string in buffer. You probably make buffer global so it is initialized to all bits 0, but a better solution is to set the null terminator explicitly after the reading loop.
to replace a sequence of 3 characters with a single one, you need to copy the rest of the string.
You can use a simple method referred as the 2 finger approach: you use 2 different index variables into the same array, one for reading, one for writing.
Here is how it works:
#include <stdio.h>
int main() {
char buffer[256];
int c;
size_t i, j, counter;
for (counter = 0; counter < sizeof(buffer) - 1; counter++) {
if ((c = getchar()) == EOF || c == '\n')
break;
buffer[counter] = c;
}
buffer[counter] = '\0';
for (i = j = 0; i < counter; i++, j++) {
if (buffer[i] == 'a' && buffer[i + 1] == 'a' && buffer[i + 2] == 'a') {
buffer[j] = '*';
i += 2;
} else {
buffer[j] = buffer[i];
}
}
buffer[j] = '\0'; /* set the null terminator, the string may be shorter */
printf("modified string: %s\n", buffer);
return 0;
}
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.