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.
Related
I am very new in C coding. I have written my code to find the longest word in a string. my code does not show any error but it prints a word with strange characters that is not in the string. Can you tell me what is wrong with my code?
Thank you
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char LongestWord (char GivenString[100]);
int main()
{
char input[100];
char DesiredWord[20];
printf("please give a string:\n");
gets(input);
DesiredWord[20]=LongestWord(input);
printf("longest Word is:%s\n",DesiredWord);
return 0;
}
char LongestWord (char GivenString[100]){
//It is a predefined function, by using this function we can clear the data from console (Monitor).
//clrscr()
int position1=0;
int position2=0;
int longest=0;
int word=0;
int Lenght=strlen(GivenString);
char Solution[20];
int p=0;
for (int i=1; i<=Lenght; i++){
if (GivenString[i-1]!=' '){
word=word++;
}
if(GivenString[i-1]=' '){
if (word>longest){
//longest stores the length of longer word
longest=word;
position2=i-1;
position1=i-longest;
word=0;
}
}
}
for (int j=position1; j<=position2; j++){
Solution[p]=GivenString[j];
p=p++;
}
return (Solution[20]);
}
This should work:
#include <stdio.h>
#include <string.h>
void LongestWord(char string[100])
{
char word[20],max[20],min[20],c;
int i = 0, j = 0, flag = 0;
for (i = 0; i < strlen(string); i++)
{
while (i < strlen(string) && string[i]!=32 && string[i]!=0)
{
word[j++] = string[i++];
}
if (j != 0)
{
word[j] = '\0';
if (!flag)
{
flag = !flag;
strcpy(max, word);
}
if (strlen(word) > strlen(max))
{
strcpy(max, word);
}
j = 0;
}
}
printf("The largest word is '%s' .\n", max);
}
int main()
{
char string[100];
printf("Enter string: ");
gets(string);
LongestWord(string);
}
Aside from invoking Undefined Behavior by returning a pointer to a locally declared array in LongestWord, using gets despite gets() is so dangerous it should never be used! and writing beyond the end of the Solution array -- you are missing the logic of identifying the longest word.
To identify the longest word, you must obtain the length of each word as you work you way down the string. You must keep track of what the longest string seen, and only if the current string is longer than the longest seen so far do you copy to valid memory that will survive the function return (and nul-terminate).
There are a number of ways to do this. You can use strtok to tokenize all words in the string, you can use a combination of strcspn and strspn to bracket the words, you can use sscanf and an offset to the beginning of each word, or what I find easiest is just to use a pair of pointers sp (start-pointer) and ep (end-pointer) to work down the string.
There you just move sp to the first character in each word and keep moving ep until you find a space (or end of string). The word length is ep - sp and then if it is the longest, you can simply use memcpy to copy length characters to your longest word buffer and nul-terminate, (repeat until you run out of characters)
To create valid storage, you have two-choices, either pass an array of sufficient size (see comment), or declare a valid block of memory within your function using malloc (or calloc or realloc) and return a pointer to that block of memory.
An example passing an array of sufficient size to hold the longest word could be:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXW 256 /* longest word buffer size */
#define MAXC 1024 /* input string buffer size */
size_t longestword (char *longest, const char *str)
{
int in = 0; /* flag reading (in/out) of word */
size_t max = 0; /* word max length */
const char *sp = str, /* start-pointer for bracketing words */
*ep = str; /* end-pointer for bracketing words */
*longest = 0; /* initialize longest as empty-string */
for (;;) { /* loop over each char in str */
if (isspace (*ep) || !*ep) { /* is it a space or end? */
if (in) { /* are we in a word? */
size_t len = ep - sp; /* if so, get word length */
if (len > max) { /* is it longest? */
max = len; /* if so, set max to len */
memcpy (longest, sp, len); /* copy len chars to longest */
longest[len] = 0; /* nul-terminate longest */
}
in = 0; /* it's a space, no longer in word */
}
if (!*ep) /* if end of string - done */
break;
}
else { /* not a space! */
if (!in) { /* if we are not in a word */
sp = ep; /* set start-pointer to current */
in = 1; /* set in flag */
}
}
ep++; /* increment end-pointer to next char */
}
return max; /* return max length */
}
int main (void) {
char str[MAXC] = "", /* storage for input string */
word[MAXW] = ""; /* storage for longest word */
size_t max = 0; /* longest word length */
fputs ("enter string: ", stdout); /* prompt */
if (!fgets (str, MAXC, stdin)) { /* validate input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
if ((max = longestword (word, str))) /* get length and longest word */
printf ("longest word: %s (%zu-chars)\n", word, max);
}
(note: by using this method you ignore all leading, trailing and intervening whitespace, so strings like " my little dog has 1 flea . " do not present problems.)
Example Use/Output
$ ./bin/longest_word
enter string: my dog has fleas
longest word: fleas (5-chars)
$ ./bin/longest_word
enter string: my little dog has 1 flea .
longest word: little (6-chars)
There are many, many ways to do this. This is one of the most basic, using pointers. You could do the same thing using indexes, e.g. string[i], etc.. That just requires you maintain an offset to the start of each word and then do the subtraction to get the length. strtok is convenient, but modifies the string being tokenized so it cannot be used with string literals or other constant strings.
Best way to learn is work the problem 3-different ways, and pick the one that you find the most intuitive. Let me know if you have further questions.
please declare a proper main entry point: int main( int argc, const char* argv[] )
Use fgets instead of gets, as gets does not check the bound of your string ( what happened when you enter a 120 chars line)
pass the length of the expected string to LongestWord
if available prefer using strnlen to plain strlen, there might be scenario where your string is not properly terminated.
Better yet use the suggested length parameter to limit your loop and break when a terminating char is encountered.
your Solution is a stack allocated array, returning it as it is might depend on your implementation, you might be better returning a heap allocated array (using malloc).
Suggested changes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* getLongestWord(char* input, size_t input_length, size_t *result_length);
int main( int argc, const char* argv[] )
{
const size_t max_length = 100;
char input[max_length]; // consider using LINE_MAX from limits.h
printf("please give a string:\n");
if ( fgets( input, max_length, stdin ) == NULL ) return EXIT_FAILURE; // some failure happened with fgets.
size_t longestWord_length = 0;
char* longestWord = getLongestWord(input, max_length , &longestWord_length);
printf("longest Word is %.*s\n",longestWord_length, longestWord );
return EXIT_SUCCESS;
}
char* getLongestWord(char* input, size_t input_length, size_t *result_length) {
char* result = NULL;
size_t length = 0;
size_t word_start = 0, word_end = 0;
for(int i = 0; i < input_length; ++i) {
if( (input[i] == ' ') || (input[i] == 0) ) {
if( i == 0 ) { // first space
word_start = 1;
continue;
}
word_end = i-1;
size_t word_length = word_end - word_start+1;
if( word_length <= length ) {
word_start = i + 1; // next word start
continue;
}
// new max length
length = word_length;
result = &input[word_start];
word_start = i + 1; // next word start
}
if( input[i] == 0 ) break; // end of string
}
*result_length = length;
return result;
}
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.
Here's a program that summarizes text. Up to this point, I'm counting the number of occurrences of each word in the text. But, I'm getting a segmentation fault in strcat.
Program received signal SIGSEGV, Segmentation fault.
0x75985629 in strcat () from C:\WINDOWS\SysWOW64\msvcrt.dll
However, while stepping through the code, the program runs the strcat() function as expected. I don't receive the error until line 75, when the program ends.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXTEXT 1000
#define MAXLINE 200
#define MAXWORDS 200
#define MAXWORD 32
char *strtolower(char *d, const char *s, size_t len);
/* summarizer: summarizes text */
int main(int argc, char *argv[]) {
/* argument check */
char *prog = argv[0];
if (argc < 1) {
fprintf(stderr, "%s: missing arguments, expected 1", prog);
exit(1);
}
/* attempt to open file */
FILE *fp;
if (!(fp = fopen(argv[1], "r"))) {
fprintf(stderr, "%s: Couldn't open file %s", prog, argv[1]);
exit(2);
}
/* read file line by line */
char line[MAXLINE], text[MAXTEXT];
while ((fgets(line, MAXTEXT, fp))) {
strncat(text, line, MAXLINE);
}
/* separate into words and count occurrences */
struct wordcount {
char *word;
int count;
};
struct wordcount words[MAXWORDS];
char *token, *delim = " \t\n.,!?";
char word[MAXWORD], textcpy[strlen(text)+1]; /*len of text and \0 */
int i, j, is_unique_word = 1;
strcpy(textcpy, text);
token = strtok(textcpy, delim);
for (i = 0; i < MAXWORDS && token; i++) {
strtolower(word, token, strlen(token));
token = strtok(NULL, delim);
/* check if word exists */
for (j = 0; words[j].word && j < MAXWORDS; j++) {
if (!strcmp(word, words[j].word)) {
is_unique_word = 0;
words[j].count++;
}
}
/* add to word list of unique */
if (is_unique_word) {
strcpy(words[i].word, word);
words[i].count++;
}
is_unique_word = 1;
}
return 0;
}
/* strtolower: copy str s to dest d, returns pointer of d */
char *strtolower(char *d, const char *s, size_t len) {
int i;
for (i = 0; i < len; i++) {
d[i] = tolower(*(s+i));
}
return d;
}
The problem is in the loop: while ((fgets(line, MAXTEXT, fp))) strncat(text, line, MAXLINE);. It is incorrect for multiple reasons:
text is uninitialized, concatenating a string to it has undefined behavior. Undefined behavior may indeed cause a crash after the end of the function, for example if the return address was overwritten.
there is no reason to use strncat() with a length of MAXLINE, the string read by fgets() has at most MAXLINE-1 bytes.
you do not check if there is enough space at the end of text to concatenate the contents of line. strncat(dest, src, n) copies at most n bytes from src to the end of dest and always sets a null terminator. It is not a safe version of strcat(). If the line does not fit at the end of text, you have unexpected behavior, and again you can observe a crash after the end of the function, for example if the return address was overwritten.
You could just try and read the whole file with fread:
/* read the file into the text array */
char text[MAXTEXT];
size_t text_len = fread(text, 1, sizeof(text) - 1, fp);
text[text_len] = '\0';
If text_len == sizeof(text) - 1, the file is potentially too large for the text array and the while loop would have caused a buffer overflow.
There is at least one problem because you create line with MAXLINE size (200), then you fgets() up to MAXTEXT (1000) chars into it.
Destination string of strncat function shall be null terminated. You need to null terminate text before passing it to strncat function. You also have to write only upto MAXLINE-1 bytes and leave a space for '\0' appended by strncat at the end to stop buffer overflow.
char line[MAXLINE], text[MAXTEXT] = {'\0'};
while ((fgets(line, MAXTEXT, fp)))
{
strncat(text, line, MAXLINE-1);
}
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.
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.