Reverses lines from a file given parameter - c

I have to write a function void reverse_file(FILE* file) that reads lines from the parameter file one line at the time into a local character array buffer whose size is #defined as a preprocessor constant MAXLINE. For each line, this function calls the previous reverse_string function to reverse the line, then writes the reversed line in standard output.
I have the reverse_string method
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void reverse_String(char* str);
int main()
{
char str [256];
strcpy_s(str, "Hello");
reverse_String(str);
return 0;
}
void reverse_String(char* str)
{
int i, j;
char temp;
i=j=temp=0;
j=strlen(str)-1;
for (i=0; i<j; i++, j--)
{
temp=str[i];
str[i]=str[j];
str[j]=temp;
}
printf("%s",str);
}
I just need help with the reverse_file(FILE* file) part.
Any help is appreciated.

Maybe something like this?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void reverse_String(char* str)
{
int i, j;
char temp;
i=j=temp=0;
j=strlen(str)-1;
for (i=0; i<j; i++, j--) {
temp=str[i];
str[i]=str[j];
str[j]=temp;
}
printf("%s",str);
}
void reverse_File(FILE* file) {
char* line;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, file)) != -1) {
reverse_String(line);
}
}
int main(void) {
FILE * fp;
fp = fopen("Maze.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
reverse_File(fp);
}

You already have a fine answer, but there are a few subtle points to consider. If you are using line-oriented input methods (fgets or getline), you should remove the '\n' or '\r\n' (DOS/windows) line-ending read and included in the original line by either fgets or getline. (this is why you have an initial blank line in the output of the original file).
A simple way to remove the line-endings is to simply overwrite the first of either line-ending encountered with a nul-terminating character. To do this, your main program logic will look like:
while (fgets (str, MAXC, fp)) { /* read each line */
rmcrlf (str); /* trim \r or \n */
strrev (str); /* reverse string */
printf ("%s\n", str); /* output reverse */
}
Your rmcrlf (remove carriage-return line-feed) could look like the following:
/** stip trailing newlines and carraige returns by overwriting with
* null-terminating char. 's' is modified in place.
*/
void rmcrlf (char *s)
{
if (!s || !*s) return; /* validate not NUL and not empty */
char *p = strpbrk (s, "\r\n"); /* locate first line-ending char */
*p = 0; /* set to nul-terminating char */
}
(note: you could use strlen (or a pointer and loop) to find the end of the string and work backwards, but strpbrk allows you to search for either '\n' or '\r' in the forward direction without needing to find the end and work backwards)
You can then reverse your string in place in the same way (but using strrchr to find the end instead of strlen)
/** strrev - reverse string 's' in place.
* The original string is not preserved.
* If 's' is not valid, no action taken.
*/
void strrev (char *s)
{
if (!s || !*s) { /* validate s not NUL and not empty */
printf ("strrev() error: invalid string\n");
return;
}
char tmp;
char *begin = s, *end = strrchr (s, 0) - 1;
while (end > begin) {
tmp = *end;
*end-- = *begin;
*begin++ = tmp;
}
}
You can put all the pieces together as follows. This is simply another way to approach the same task. Note: the following code uses an enum instead of a #define to set define the constant (just to show the alternate method). Also note: that the code will read from the file given as the first argument (or from stdin by default, if no filename is given):
#include <stdio.h>
#include <string.h>
enum { MAXC = 256 };
void strrev (char *s);
void rmcrlf (char *s);
int main (int argc, char **argv) {
char str[MAXC] = "";
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file is open */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (str, MAXC, fp)) { /* read each line */
rmcrlf (str); /* trim \r or \n */
strrev (str); /* reverse string */
printf ("%s\n", str); /* output reverse */
}
if (fp != stdin) /* close file (if not stdin) */
fclose (fp);
return 0;
}
/** strrev - reverse string 's' in place.
* The original string is not preserved.
* If 's' is not valid, no action taken.
*/
void strrev (char *s)
{
if (!s || !*s) { /* validate s not NUL and not empty */
printf ("strrev() error: invalid string\n");
return;
}
char tmp;
char *begin = s, *end = strrchr (s, 0) - 1;
while (end > begin) {
tmp = *end;
*end-- = *begin;
*begin++ = tmp;
}
}
/** stip trailing newlines and carraige returns by overwriting with
* null-terminating char. 's' is modified in place.
*/
void rmcrlf (char *s)
{
if (!s || !*s) return;
char *p = strpbrk (s, "\r\n");
*p = 0;
}
Example Input
$ cat dat/strtorev.txt
abc def ghi jkl mno pqr
ABC DEF GHI JKL MNO PQR
Output
$ ./bin/strrev_line <dat/strtorev.txt
rqp onm lkj ihg fed cba
RQP ONM LKJ IHG FED CBA
(note: as Johnathan asked in the comment, you can either reverse the line as shown above, or you can reverse each word (e.g. cba fed ihg...) make sure you have correctly stated what you need to do, if not we can help further).
Let me know if you have any questions.

Related

removing trailing and leading spaces from a file

I am trying to read lines from a text file of unknown length.
In the line there can be leading and trailing white-spaces until the string occurs.
So my first step is to read line by line and allocate memory for the strings. Then remove all the leading and trailing white spaces.
After that I want to check if the string has any white space characters in it which is an invalid character. For example the string can not look like this "bad string" but can look like this "goodstring".
However when I call the function to remove the leading and trailing white spaces it also removes characters before or after a white space.
Could someone tell me what I am doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define NCHAR 64
char *readline (FILE *fp, char **buffer);
char *strstrip(char *s);
int main (int argc, char **argv) {
char *line = NULL;
size_t idx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (readline (fp, &line)) { /* read each line in 'fp' */
printf (" line[%2zu] : %s\n", idx++, line);
free (line);
line = NULL;
}
if (fp != stdin) fclose (fp);
return 0;
}
/* read line from 'fp' allocate *buffer NCHAR in size
* realloc as necessary. Returns a pointer to *buffer
* on success, NULL otherwise.
*/
char *readline (FILE *fp, char **buffer)
{
int ch;
size_t buflen = 0, nchar = NCHAR;
size_t n;
char *invalid_character = " ";
*buffer = malloc (nchar); /* allocate buffer nchar in length */
if (!*buffer) {
fprintf (stderr, "readline() error: virtual memory exhausted.\n");
return NULL;
}
while ((ch = fgetc(fp)) != '\n' && ch != EOF)
{
(*buffer)[buflen++] = ch;
if (buflen + 1 >= nchar) { /* realloc */
char *tmp = realloc (*buffer, nchar * 2);
if (!tmp) {
fprintf (stderr, "error: realloc failed, "
"returning partial buffer.\n");
(*buffer)[buflen] = 0;
return *buffer;
}
*buffer = tmp;
nchar *= 2;
}
strstrip(*buffer); //remove traiing/leading spaces
}
(*buffer)[buflen] = 0; /* nul-terminate */
if (invalid_character[n = strspn(invalid_character, *buffer)] == '\0') //check if a string has invalid character ' ' in it
{
puts(" invalid characters");
}
if (buflen == 0 && ch == EOF) { /* return NULL if nothing read */
free (*buffer);
*buffer = NULL;
}
return *buffer;
}
char *strstrip(char *s)
{
size_t size;
char *end;
size = strlen(s);
if (!size)
return s;
end = s + size - 1;
while (end >= s && isspace(*end))
end--;
*(end + 1) = '\0';
while (*s && isspace(*s))
s++;
return s;
}
You do not need to worry about the length of the string passed to strstrip(), simply iterate over all characters in the string removing whitespace characters, e.g. the following version removals ALL whitespace from s:
/** remove ALL leading, interleaved and trailing whitespace, in place.
* the original start address is preserved but due to reindexing,
* the contents of the original are not preserved. returns pointer
* to 's'. (ctype.h required)
*/
char *strstrip (char *s)
{
if (!s) return NULL; /* valdiate string not NULL */
if (!*s) return s; /* handle empty string */
char *p = s, *wp = s; /* pointer and write-pointer */
while (*p) { /* loop over each character */
while (isspace ((unsigned char)*p)) /* if whitespace advance ptr */
p++;
*wp++ = *p; /* use non-ws char */
if (*p)
p++;
}
*wp = 0; /* nul-terminate */
return s;
}
(note: if the argument to isspace() is type char, a cast to unsigned char is required, see NOTES Section, e.g. man 3 isalpha)
Removing only Excess Whitespace
The following version removes leading and trailing whitespace and collapses multiple sequences of whitespace to a single space:
/** remove excess leading, interleaved and trailing whitespace, in place.
* the original start address is preserved but due to reindexing,
* the contents of the original are not preserved. returns pointer
* to 's'. (ctype.h required) NOTE: LATEST
*/
char *strstrip (char *s)
{
if (!s) return NULL; /* valdiate string not NULL */
if (!*s) return s; /* handle empty string */
char *p = s, *wp = s; /* pointer and write-pointer */
while (*p) {
if (isspace((unsigned char)*p)) { /* test for ws */
if (wp > s) /* ignore leading ws, while */
*wp++ = *p; /* preserving 1 between words */
while (*p && isspace (unsigned char)(*p)) /* skip remainder */
p++;
if (!*p) /* bail on end-of-string */
break;
}
if (*p == '.') /* handle space between word and '.' */
while (wp > s && isspace ((unsigned char)*(wp - 1)))
wp--;
*wp++ = *p; /* use non-ws char */
p++;
}
while (wp > s && isspace ((unsigned char)*(wp - 1))) /* trim trailing ws */
wp--;
*wp = 0; /* nul-terminate */
return s;
}
(note: s must be mutable and therefore cannot be a string-literal)

Copying a line from a csv file using pointers

I'm writing a program that is supposed to search for a name in a CSV file and copy the record (all the info on the same line as the name) that goes along with it.
For example, if CSV file contains:
Bob, 13, 12345612
Eli, 12, 21398743
I would input "Bob" to get the first line, and copy this into an array called "record".
So far my code is as follows:
#include<stdio.h>
#include<stdlib.h>
void FindRecord(char *a, char *b, char c[]);
void main(void){
char arrayName[100];
char arrayNewname[100];
char *name = arrayName;
char *newname = arrayNewname;
char record[1000];
printf("Please input a name in the phonebook: ");
scanf("%s", name);
printf("Please input a replacement name: ");
scanf("%s", newname);
FindRecord("phonebook.csv",name,record);
}
void FindRecord(char *filename, char *name, char record[]){
//Create temp array of max size
char temp[1000];
//Open file
FILE *f = fopen(filename, "r");
//Make sure file exists
if (f == NULL){
printf("File does not exist");
fclose(f);
exit(1);
}
//While
while(!feof(f)){
//Read one line at a time
fgets(temp, 1000, f);
int i = 0;
int *p;
for(int i = 0; i < 1000; i++){
if(temp[i] == *name){
record[i] = temp[i];
name++;
}
size_t n = (sizeof record /sizeof record[0]);
if(temp[i] == *name){
*p = temp[i + n];
}
}
}
printf("%s", record);
fclose(f);
}
Basically, I've found Bob and copied Bob, but do not understand how to proceed using pointers (not allowed to use string.h) and copying the rest of the line. I've been trying to play around with the length of the word once Ive found it but this isn't working either because of pointers. Any help/hints would be appreciated.
When you are not allowed to use string.h you have to compare strings char by char.
Start by having a loop to find the name.
See: Compare two strings character by character in C
And then copy the rest of the record. Either char by char or with more advance functions like memcpy:
https://www.techonthenet.com/c_language/standard_library_functions/string_h/memcpy.php
Now who is int *p ?
You didn't initialized this pointer. You have to either malloc it or assign it to an existing memory.
See for more information:
https://pebble.gitbooks.io/learning-c-with-pebble/content/chapter08.html
I think you should read more about pointers and then things will work much better for you.
Not being allowed to use string.h is a good exercise in computing the string lengths manually (necessary to remove the trailing '\n' from the lines read with fgets) and also a good exercise for manual string comparisons.
For example, if you are reading lines into buf, you can use a simple for loop to get the length of buf, e.g.
int blen = 0;
for (; buf[blen]; blen++) {} /* get line length */
(note: you find the length of name, say nlen in a similar manner)
Then having the length in blen, you can easily check that the final character in buf is the '\n' character and remove it by overwriting the newline with the nul-terminating character, e.g.
if (blen && buf[blen - 1] == '\n') /* check/remove '\n' */
buf[--blen] = 0; /* overwrite with '\0' */
The remainder of your findrecord function is just a matter of iterating forward over each character looking for the character that is the first character in name. Once found, you simply compare then next nlen character to see if you have found name in buf. You can easily do that with:
char *np = name, /* pointer to name */
*bp = p; /* current pointer in buf */
...
for (i = 0; /* compre name in buf */
i < nlen && *np && *bp && *np == *bp;
i++, np++, bp++) {}
/* validate nlen chars match in buf */
if (np - name == nlen && *(np-1) == *(bp-1)) {
One you have validated you found name in buf, simply copy buf to record insuring your nul-terminate record when done copying buf, e.g.
if (np - name == nlen && *(np-1) == *(bp-1)) {
bp = buf;
for (i = 0; buf[i]; i++) /* copy buf to record */
record[i] = buf[i];
record[i] = buf[i]; /* nul-terminate */
return record; /* return record */
}
Putting it altogether, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
enum { MAXC = 100, MAXL = 1000 }; /* if you need constants, define them */
char *findrecord (FILE *fp, char *name, char *record);
/* main is type 'int', and has arguments -- use them */
int main (int argc, char **argv) {
char name[MAXC] = "",
replace[MAXC] = "",
record[MAXL] = "",
*matched;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : NULL;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* prompt, read, validate name */
printf ("Please input a name in the phonebook: ");
if (scanf ("%99[^\n]%*c", name) != 1) {
fprintf (stderr, "error: invalid input - name.\n");
return 1;
}
/* prompt, read, validate replace */
printf("Please input a replacement name: ");
if (scanf ("%99[^\n]%*c", replace) != 1) {
fprintf (stderr, "error: invalid input - replace.\n");
return 1;
}
/* search name, copy record, return indicates success/failure */
matched = findrecord (fp, name, record);
if (fp != stdin) fclose (fp); /* close file if not stdin */
if (matched) { /* if name matched */
printf ("record : '%s'\n", record);
}
return 0; /* main() returns a value */
}
char *findrecord (FILE *fp, char *name, char *record){
char buf[MAXL] = ""; /* buf for line */
while (fgets (buf, MAXL, fp)) { /* for each line */
char *p = buf;
int blen = 0;
for (; buf[blen]; blen++) {} /* get line length */
if (blen && buf[blen - 1] == '\n') /* check/remove '\n' */
buf[--blen] = 0; /* overwrite with '\0' */
for (; *p && *p != '\n'; p++) /* for each char in line */
if (*p == *name) { /* match start of name? */
char *np = name, /* pointer to name */
*bp = p; /* current pointer in buf */
int i = 0, /* general 'i' var */
nlen = 0; /* name length var */
for (nlen = 0; name[nlen]; nlen++) {} /* name length */
for (i = 0; /* compre name in buf */
i < nlen && *np && *bp && *np == *bp;
i++, np++, bp++) {}
/* validate nlen chars match in buf */
if (np - name == nlen && *(np-1) == *(bp-1)) {
bp = buf;
for (i = 0; buf[i]; i++) /* copy buf to record */
record[i] = buf[i];
record[i] = buf[i]; /* nul-terminate */
return record; /* return record */
}
}
}
return NULL; /* indicate no match in file */
}
Example Use/Output
$ ./bin/findrec dat/bob.csv
Please input a name in the phonebook: Bob
Please input a replacement name: Sam
record : 'Bob, 13, 12345612'
Non-Match Example
$ ./bin/findrec dat/bob.csv
Please input a name in the phonebook: Jerry
Please input a replacement name: Bob
Look things over and let me know if you have further questions.

Reading words separately from file

I'm trying to make a program that scans a file containing words line by line and removes words that are spelled the same if you read them backwards (palindromes)
This is the program.c file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "header.h"
int main(int argc, char **argv)
{
if(argc != 3)
{
printf("Wrong parameters");
return 0;
}
FILE *data;
FILE *result;
char *StringFromFile = (char*)malloc(255);
char *word = (char*)malloc(255);
const char *dat = argv[1];
const char *res = argv[2];
data = fopen(dat, "r");
result =fopen(res, "w");
while(fgets(StringFromFile, 255, data))
{
function1(StringFromFile, word);
fputs(StringFromFile, result);
}
free(StringFromFile);
free (word);
fclose(data);
fclose(result);
return 0;
}
This is the header.h file:
#ifndef HEADER_H_INCLUDEC
#define HEADER_H_INCLUDED
void function1(char *StringFromFile, char *word);
void moving(char *StringFromFile, int *index, int StringLength, int WordLength);
#endif
This is the function file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "header.h"
void function1(char *StringFromFile, char *word)
{
int StringLength = strlen(StringFromFile);
int WordLength;
int i;
int p;
int k;
int t;
int m;
int match;
for(i = 0; i < StringLength; i++)
{ k=0;
t=0;
m=i;
if (StringFromFile[i] != ' ')
{ while (StringFromFile[i] != ' ')
{
word[k]=StringFromFile[i];
k=k+1;
i=i+1;
}
//printf("%s\n", word);
WordLength = strlen(word)-1;
p = WordLength-1;
match=0;
while (t <= p)
{
if (word[t] == word[p])
{
match=match+1;
}
t=t+1;
p=p-1;
}
if ((match*2) >= (WordLength))
{
moving(StringFromFile, &m, StringLength, WordLength);
}
}
}
}
void moving(char *StringFromFile, int *index, int StringLength, int WordLength)
{ int i;
int q=WordLength-1;
for(i = *index; i < StringLength; i++)
{
StringFromFile[i-1] = StringFromFile[i+q];
}
*(index) = *(index)-1;
}
It doesn't read each word correctly, though.
This is the data file:
abcba rttt plllp
aaaaaaaaaaaa
ababa
abbbba
kede
These are the separate words the program reads:
abcba
rttta
plllp
aaaaaaaaaaaa
ababa
abbbba
kede
This is the result file:
abcba rtttp
kede
It works fine if there is only one word in a single line, but it messes up when there are multiple words. Any help is appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "header.h"
# define MAX 255
int Find_Number_Words_in_Line( char str[MAX] )
{
char *ptr;
int count = 0;
int j;
/* advance character pointer ptr until end of str[MAX] */
/* everytime you see the space character, increase count */
/* might not always work, you'll need to handle multiple space characters before/between/after words */
ptr = str;
for ( j = 0; j < MAX; j++ )
{
if ( *ptr == ' ' )
count++;
else if (( *ptr == '\0' ) || ( *ptr == '\n' ))
break;
ptr++;
}
return count;
}
void Extract_Word_From_Line_Based_on_Position( char line[MAX], char word[MAX], const int position )
{
char *ptr;
/* move pointer down line[], counting past the number of spaces specified by position */
/* then copy the next word from line[] into word[] */
}
int Is_Palindrome ( char str[MAX] )
{
/* check if str[] is a palindrome, if so return 1, else return 0 */
}
int main(int argc, char **argv)
{
FILE *data_file;
FILE *result_file;
char *line_from_data_file = (char*)malloc(MAX);
char *word = (char*)malloc(MAX);
const char *dat = argv[1];
const char *res = argv[2];
int j, n;
if (argc != 3)
{
printf("Wrong parameters");
return 0;
}
data_file = fopen(dat, "r");
result_file = fopen(res, "w");
fgets( line_from_data_file, MAX, data_file );
while ( ! feof( data_file ) )
{
/*
fgets returns everything up to newline character from data_file,
function1 in original context would only run once for each line read
from data_file, so you would only get the first word
function1( line_from_data_file, word );
fputs( word, result_file );
fgets( line_from_data_file, MAX, data_file );
instead try below, you will need to write the code for these new functions
don't be afraid to name functions in basic English for what they are meant to do
make your code more easily readable
*/
n = Find_Number_Words_in_Line( line_from_data_file );
for ( j = 0; j < n; j++ )
{
Extract_Word_From_Line_Based_on_Position( line_from_data_file, word, n );
if ( Is_Palindrome( word ) )
fputs( word, result_file ); /* this will put one palindrome per line in result file */
}
fgets( line_from_data_file, MAX, data_file );
}
free( line_from_data_file );
free( word );
fclose( data_file );
fclose( result_file );
return 0;
}
To follow up from the comments, you may be overthinking the problem a bit. To check whether each word in each line of a file is a palindrome, you have a 2 part problem. (1) reading each line (fgets is fine), and (2) breaking each line into individual words (tokens) so that you can test whether each token is a palindrome.
When reading each line with fgets, a simple while loop conditioned on the return of fgets will do. e.g., with a buffer buf of sufficient size (MAXC chars), and FILE * stream fp open for reading, you can do:
while (fgets (buf, MAXC, fp)) { /* read each line */
... /* process line */
}
(you can test the length of the line read into buf is less than MAXC chars to insure you read the complete line, if not, any unread chars will be placed in buf on the next loop iteration. This check, and how you want to handle it, is left for you.)
Once you have your line read, you can either use a simple pair of pointers (start and end pointers) to work your way through buf, or you can use strtok and let it return a pointer to the beginning of each word in the line based on the set of delimiters you pass to it. For example, to split a line into words, you probably want to use delimiters like " \t\n.,:;!?" to insure you get words alone and not words with punctuation (e.g. in the line "sit here.", you want "sit" and "here", not "here.")
Using strtok is straight forward. On the first call, you pass the name of the buffer holding the string to be tokenized and a pointer to the string containing the delimiters (e.g. strtok (buf, delims) above), then for each subsequent call (until the end of the line is reached) you use NULL as name of the buffer (e.g. strtok (NULL, delims)) You can either call it once and then loop until NULL is returned, or you can do it all using a single for loop given that for allows setting an initial condition as part of the statement, e.g., using separate calls:
char *delims = " \t\n.,:;"; /* delimiters */
char *p = strtok (buf, delims); /* first call to strtok */
while ((p = strtok (NULL, delims))) { /* all subsequent calls */
... /* check for palindrome */
}
Or you can simply make the initial call and all subsequent calls in a for loop:
/* same thing in a single 'for' statement */
for (p = strtok (buf, delims); p; p = strtok (NULL, delims)) {
... /* check for palindrome */
}
Now you are to the point you need to check for palindromes. That is a fairly easy process. Find the length of the token, then either using string indexes, or simply using a pointer to the first and last character, work from the ends to the middle of each token making sure the characters match. On the first mismatch, you know the token is not a palindrome. I find a start and end pointer just as easy as manipulating sting indexes, e.g. with the token in s:
char *ispalindrome (char *s) /* function to check palindrome */
{
char *p = s, /* start pointer */
*ep = s + strlen (s) - 1; /* end pointer */
for ( ; p < ep; p++, ep--) /* work from end to middle */
if (*p != *ep) /* if chars !=, not palindrome */
return NULL;
return s;
}
If you put all the pieces together, you can do something like the following:
#include <stdio.h>
#include <string.h>
enum { MAXC = 256 }; /* max chars for line buffer */
char *ispalindrome (char *s);
int main (int argc, char **argv) {
char buf[MAXC] = "", /* line buffer */
*delims = " \t\n.,:;"; /* delimiters */
unsigned ndx = 0; /* line index */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
char *p = buf; /* pointer to pass to strtok */
printf ("\n line[%2u]: %s\n tokens:\n", ndx++, buf);
for (p = strtok (buf, delims); p; p = strtok (NULL, delims))
if (ispalindrome (p))
printf (" %-16s - palindrome\n", p);
else
printf (" %-16s - not palindrome\n", p);
}
if (fp != stdin) fclose (fp);
return 0;
}
char *ispalindrome (char *s) /* function to check palindrome */
{
char *p = s, *ep = s + strlen (s) - 1; /* ptr & end-ptr */
for ( ; p < ep; p++, ep--) /* work from end to middle */
if (*p != *ep) /* if chars !=, not palindrome */
return NULL;
return s;
}
Example Input
$ cat dat/palins.txt
abcba rttt plllp
aaaaaaaaaaaa
ababa
abbbba
kede
Example Use/Output
$ ./bin/palindrome <dat/palins.txt
line[ 0]: abcba rttt plllp
tokens:
abcba - palindrome
rttt - not palindrome
plllp - palindrome
line[ 1]: aaaaaaaaaaaa
tokens:
aaaaaaaaaaaa - palindrome
line[ 2]: ababa
tokens:
ababa - palindrome
line[ 3]: abbbba
tokens:
abbbba - palindrome
line[ 4]: kede
tokens:
kede - not palindrome
Look things over and think about what it taking place. As mentioned above, insuring you have read a complete line in each call with fgets should be validated, that is left to you. (but with this input file -- of course it will) If you have any questions, let me know and I'll be happy to help further.

Using strcat to concatenate lines from a file

I'm trying to read a file and a lines next line, to see if it starts with a space, for instance:
First line
second line
Third line
fourth line
Where when I'm reading the file in, I want to check and see if the following line, has a space, if it does, I want to strcat the two lines (in this case the first and second line).
So for the first example:
1.) Read in the first line, read ahead to the second, see that their is a space, and strcat both strings, making "First linesecond line" (Repeat for any other lines that follow this pattern).
Here's my go at it:
int main(void) {
FILE * file = fopen("file.txt","r");
if(file==NULL) { return 0; }
char * fileBuffer = malloc(sizeof(char)*100);
char * temp = malloc(sizeof(char)*100);
while(fgets(fileBuffer,100,file) != NULL) {
if(isspace(fileBuffer[0])) {
strcpy(temp,fileBuffer);
//store line that has a space in static temporary variable
}
else {
if(strcmp(temp,"") != 0) { //if temp is not empty
strcat(fileBuffer,temp);
}
}
}
free(fileBuffer);
free(temp);
return 0;
}
However this doesn't work. What happens is when fgets gets executed, it reads the first line, it sees there is no white space, and then goes to the next line, sees there is white space, stores it, but now fileBuffer doesn't contain the first line anymore, but the second.
So when I strcat the next time,I don't get the right result of "First linesecond line".
Instead i get the result of the third line mixed with the second, which is not what I want.
I'm not exactly sure how to fix my logic here, any ideas?
fix your logic like this:
#define LINE_SIZE 100
int main(void) {
FILE * file = fopen("file.txt","r");
if(file==NULL) { perror("fopen"); return -1; }
char * fileBuffer = malloc(LINE_SIZE);
char * temp = malloc(LINE_SIZE * 2);//Binding of the string is only once
while(fgets(fileBuffer, LINE_SIZE, file) != NULL) {
if(isspace(fileBuffer[0])) {
temp[strcspn(temp, "\n")] = 0;//remove newline
strcat(temp, &fileBuffer[1]);
printf("%s", temp);
}
else {
strcpy(temp, fileBuffer);
}
}
fclose(file);
free(fileBuffer);
free(temp);
return 0;
}
You have several additional considerations you will need to deal with. The first being, you need to remove the trailing '\n' include in the read by fgets. To do that, you will need the length of the line read. You can then remove the trialing '\n' by overwriting it with a nul-terminating character. e.g.:
while (fgets (buf1, MAXC, fp)) {
size_t len1 = strlen (buf1); /* get length */
if (len1 && buf1[len1-1] == '\n') buf1[--len1] = 0; /* remove \n */
Another consideration is how to manage allocation and freeing of the memory you use for the combined line. Since you are reading both the first part and then second part of your final line from relatively fixed length strings, it would make more sense to use 2 static buffers, one for reading the line, and one for holding a copy of the first. You can allocate for the final result. e.g.
enum { MAXC = 100 }; /* constant for max characters */
...
int main (int argc, char **argv) {
char buf1[MAXC] = {0};
char buf2[MAXC] = {0};
char *both = NULL;
Once you have both parts of your line ready to combine, you can allocate exactly the space needed, e.g.
if (*buf1 == ' ' && *buf2) {
both = malloc (len1 + strlen (buf2) + 1);
strcpy (both, buf2);
strcat (both, buf1);
...
Don't forget to free both after each allocation. Putting the pieces together, and fixing the logic of your comparisons, you could end up with a solution like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 100 };
int main (int argc, char **argv) {
char buf1[MAXC] = {0};
char buf2[MAXC] = {0};
char *both = NULL;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n,", argv[1]);
return 1;
}
while (fgets (buf1, MAXC, fp)) {
size_t len1 = strlen (buf1);
if (len1 && buf1[len1-1] == '\n') buf1[--len1] = 0;
if (*buf1 == ' ' && *buf2) {
both = malloc (len1 + strlen (buf2) + 1);
strcpy (both, buf2);
strcat (both, buf1);
printf ("'%s'\n", both);
free (both);
*buf2 = 0;
}
else
strcpy (buf2, buf1);
}
if (fp != stdin) fclose (fp);
return 0;
}
Output
$ ./bin/cpcat ../dat/catfile.txt
'First line second line'
'Third line fourth line'
Look it over and let me know if you have any questions.
Really a quick and dirty way
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main(void) {
FILE * file = fopen("file.txt", "r");
if (file == NULL) { return 0; }
char fileBuffer[100];
char temp[100];
int first = 1;
while (fgets(fileBuffer, 100, file) != NULL) {
if (isspace(fileBuffer[0])) {
strcat(temp, fileBuffer);
//store line that has a space in static temporary variable
}
else {
if (first == 1){
strncpy(temp, fileBuffer, sizeof(temp));
// Remove the end line
temp[strlen(temp) - 1] = 0;
strcat(temp, " ");
first = 0;
}
else{
if (strcmp(temp, "") != 0) { //if temp is not empty
strcat(temp, fileBuffer);
// Remove the end line
temp[strlen(temp) - 1] = 0;
strcat(temp, " ");
}
}
}
}
printf("%s", temp);
free(fileBuffer);
free(temp);
return 0;
}
Output:

Multiple Command-Line Arguments - Replace Words

I've a program which takes any number of words from the command-line arguments and replaces them with the word 'CENSORED'. I finally have the program working for the first argument passed in, and I am having trouble getting the program to censor all arguments, outputted in just a single string. The program rather functions individually on a given argument and does not take them all into account. How would I modify this?
How does one use/manipulate multiple command-line arguments collectively ?
My code follows.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *replace_str(char *str, char *orig, char *rep, int j, int argc)
{
static char buffer[4096];
char *p;
for ( j = 1; j <= argc; j++ )
{
if(!(p = strstr(str, orig))) // Check if 'orig' is not in 'str'
{
if ( j == argc ) { return str; } // return str once final argument is reached
else { continue; } // restart loop with next argument
}
strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' str
buffer[p-str] = '\0';
if ( j == argc ) { return buffer; }
else { continue; }
}
sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));
}
int main( int argc, char* argv[] ) //argv: list of arguments; array of char pointers //argc: # of arguments.
{
long unsigned int c, i = 0, j = 1;
char str[4096];
while ( (c = getchar()) != EOF )
{
str[i] = c; // save input string to variable 'str'
i++;
}
puts(replace_str( str, argv[j], "CENSORED", j, argc ) );
return 0;
}
i.e.
$ cat Hello.txt
Hello, I am me.
$ ./replace Hello me < Hello.txt
CENSORED, I am CENSORED.
Two issues, you are not guaranteeing a null-terminated str and second, you are not iterating over the words on the command line to censor each. Try the following in main after your getchar() loop:
/* null-terminate str */
str[i] = 0;
/* you must check each command line word (i.e. argv[j]) */
for (j = 1; j < argc; j++)
{
puts(replace_str( str, argv[j], "CENSORED", j, argc ) );
}
Note: that will place each of the CENSORED words on a separate line. As noted in the comments, move puts (or preferably printf) outside the loop to keep on a single line.
Edit
I apologize. You have more issues than stated above. Attempting to check the fix, it became apparent that you would continue to have difficulty parsing the words depending on the order the bad words were entered on the command line.
While it is possible to do the pointer arithmetic to copy/expand/contract the original string regardless of the order the words appear on the command line, it is far easier to simply separate the words provided into an array, and then compare each of the bad words against each word in the original string.
This can be accomplished relatively easily with strtok or strsep. I put together a quick example showing this approach. (note: make a copy of the string before passing to strtok, as it will alter the original). I believe this is what you were attempting to do, but you were stumbling on not having the ability to compare each word (thus your use of strstr to test for a match).
Look over the example and let me know if you have further questions. Note: I replaced your hardcoded 4096 with a SMAX define and provided a word max WMAX for words entered on the command line. Also always initialize your strings/buffers. It will enable you to always be able to easily find the last char in the buffer and ensure the buffer is always null-terminated.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SMAX 4096
#define WMAX 50
char *replace_str (char *str, char **bad, char *rep)
{
static char buffer[SMAX] = {0};
char *p = buffer;
char *wp = NULL;
unsigned i = 0;
unsigned char censored = 0;
char *str2 = strdup (str); /* make copy of string for strtok */
char *savp = str2; /* and save start address to free */
if (!(wp = strtok (str2, " "))) /* get first word in string or bail */
{
if (savp) free (savp);
return str;
}
while (bad[i]) /* test against each bad word */
{
if (strcmp (wp, bad[i++]) == 0) /* if matched, copy rep to buffer */
{
memcpy (buffer, rep, strlen (rep));
censored = 1;
}
}
if (!censored) /* if no match, copy original word */
memcpy (buffer, wp, strlen (wp));
while ((wp = strtok (NULL, " "))) /* repeat for each word in str */
{
i = 0;
censored = 0;
memcpy (strchr (buffer, 0), " ", 1);
p = strchr (buffer, 0); /* (get address of null-term char) */
while (bad[i])
{
if (strcmp (wp, bad[i++]) == 0)
{
memcpy (p, rep, strlen (rep));
censored = 1;
}
}
if (!censored)
memcpy (p, wp, strlen (wp));
}
if (savp) free (savp); /* free copy of strtok string */
return buffer;
}
int main ( int argc, char** argv)
{
unsigned int i = 0;
char str[SMAX] = {0};
char *badwords[WMAX] = {0}; /* array to hold command line words */
for (i = 1; i < argc; i++) /* save command line in array */
badwords[i-1] = strdup (argv[i]);
i = 0; /* print out the censored words */
printf ("\nCensor words:");
while (badwords[i])
printf (" %s", badwords[i++]);
printf ("\n\n");
printf ("Enter string: "); /* promt to enter string to censor */
if (fgets (str, SMAX-1, stdin) == NULL)
{
fprintf (stderr, "error: failed to read str from stdin\n");
return 1;
}
str[strlen (str) - 1] = 0; /* strip linefeed from input str */
/* print out censored string */
printf ("\ncensored str: %s\n\n", replace_str (str, badwords, "CENSORED"));
i = 0; /* free all allocated memory */
while (badwords[i])
free (badwords[i++]);
return 0;
}
use/output
./bin/censorw bad realbad
Censor words: bad realbad
Enter string: It is not nice to say bad or realbad words.
censored str: It is not nice to say CENSORED or CENSORED words.

Resources