How to reassign a string? - c

I am trying to write a program which merges a lines from stdin and print only those sentences which are longer than 80 characters. The first found line works well - the later ones, however, are empty. I think that I am doing something wrong with the line
current_sentence = malloc(sentence_len);.
How can I reassign a string correctly?
Code
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define BUFFERSIZE 100
char* merge_string(char *text[], int n){
int i;
char *result = malloc(BUFFERSIZE * n);
for (i=0; i < n; i++){
strcat(result, text[i]);
}
return result;
}
int main(int argc, char *argv[]){
char buffer[BUFFERSIZE];
int i = 0;
char *text[BUFFERSIZE];
while(fgets(buffer, BUFFERSIZE, stdin) != NULL){
text[i] = strdup(buffer);
i++;
}
char *sentence = merge_string(text, i);
int sentence_len = strlen(sentence);
int j = 0;
int counter = 0;
char *current_sentence = malloc(sentence_len);
while (j < sentence_len){
current_sentence[counter] = sentence[j];
if (sentence[j] == '\n' && counter >= 80){
printf(":::HIT:::%s\n\n\n", current_sentence);
counter = 0;
current_sentence = malloc(sentence_len);
}
else if (sentence[j] == '\n'){
puts("Resetting counter");
counter = 0;
}
j++; counter++;
}
return 0;
}
Output
make 1_17; ./1_17 < example.txt
make: `1_17' is up to date.
Resetting counter
Resetting counter
:::HIT:::SHenri Cartier-Bresson (1908-2004) said "Your first 10,000 photographs are your worst," but he shot more than one an hour.)
Resetting counter
:::HIT:::
Resetting counter
:::HIT:::

You are not terminating current_sentence with a null character ('\0'). If you want printf to print the string properly, better make sure it is null-terminated.
By the way, there's no need for a second malloc. Reuse the memory allocated for current_sentence without re-allocating.
Also note that you're not freeing the allocated memory properly. You should be use a matching free call for each malloc. Perhaps this isn't a problem now, but it creates a memory leak.
Your loop should look something like this:
while (j < sentence_len)
{
current_sentence[counter] = sentence[j];
if (sentence[j] == '\n')
{
if (counter >= 80)
{
current_sentence[counter + 1] = '\0'; // Make string null-terminated
printf(":::HIT:::%s\n\n\n", current_sentence);
}
else
{
puts("Resetting counter");
}
counter = 0;
}
else
{
counter++;
}
j++;
}
free(current_sentence); // Free allocated memory
Then again, as mentioned in a comment, you'd rather let fgets do the work for you indeed.

char *text[BUFFERSIZE];
should be
char text[BUFFERSIZE];

Related

Removing consecutive repeated characters from string using C

I'm trying to remove consecutive repeated characters from a given string.
Example:
bssdffFdcrrrtttii ***#
output is supposed to be:
bsdfFdcrti *#
This code doesn't work and only prints the first char (b), I want to learn about my mistake.
when I'm doing a printf test, it works but not for spaces.
I think the problem might be with the new char array.
void Ex6() {
char* string[80];
scanf("%s", &string);
puts(removeDup(string));
}
char* removeDup(char *string) {
int i, c = 0;
char* newString[80];
for (i = 0; i < strlen(string); i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
return newString;
}
There are several problems with your program:
The declaration of newString should be char newString[80], i.e., an array of characters and not an array of pointers-to-characters, and likewise for the declaration in Ex6.
The call to scanf should then be scanf("%s", string), since string is already the address of an array of characters, but...
Use fgets to read a string from the user to ensure that you read whitespace, if it's important, and that the buffer is not exceeded.
newString is allocated on the stack and so should not be returned to the caller. It is better to do a char *newString = strdup(string), or, slightly less sloppy, char *newString = malloc(strlen(string)+1), which will call malloc for a block of memory sufficient to hold the original string, and thus the version without duplicates -- the comments rightly point out that this could be optimized. In principle, the caller, i.e., Ex6, must free the returned pointer to avoid a memory leak but it hardly matters in such a short program.
The result needs a null terminator: newString[c] = '\0'.
Otherwise, the removeDup function seems to work correctly.
So, putting all of that together:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* removeDup(const char *string)
{
size_t i, c = 0;
size_t string_len = strlen(string);
char *newString = malloc(string_len + 1);
for (i = 0; i < string_len; i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
return newString;
}
#define MAX_STRING_LEN 80
void Ex6() {
char string[MAX_STRING_LEN];
char* result;
if (fgets(string, MAX_STRING_LEN, stdin) != NULL) {
result = removeDup(string);
printf("%s", result);
free(result);
}
}
Finally, I agree with #tadman's comment. Since the input string must anyway be traversed to calculate the length, we may as well optimize the size of the result string:
char* removeDup(const char *string)
{
size_t i, c = 0;
char *newString;
for (i = 0; string[i] != '\0'; i++)
c += (string[i] != string[i + 1]);
newString = malloc(c + 1);
for (i = c = 0; string[i] != '\0'; i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
return newString;
}
There are quite a few issues in your program. It wouldn't even compile let alone run. Also, the most problematic issue is that you are returning a pointer to a local variable from a function that ceases its scope upon completion. A simplified version of your program is as follows:
void Ex6()
{
char string[80];
scanf("%s", string);
int i, c = 0;
char newString[80];
for (i = 0; i < strlen(string); i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
puts(newString);
}
You can do it with O(n) time and O(1) space, by modifying existing string:
#include <stdio.h>
char* removeDup(char* input) {
char* newTail = input, *oldTail = input;
while (*oldTail) {
if (*newTail == *oldTail) {
++oldTail;
} else {
*++newTail = *oldTail++;
}
}
return newTail;
}
int main() {
char string[] = "bssdffFdcrrrtttii ***#";
char* newEnd = removeDup(string);
char* tmp = string;
while (tmp != newEnd) {
printf("%c", *tmp++);
}
//Print the last char if string had any duplicates
if(*tmp) {
printf("%c", *tmp++);
}
return 0;
}

Need String initialization advice

I got an assignment for wich i have to write an program that will take the letters in the first parameter string, and find them in the second parameter string like so:
./a.out "lolabab" "ablcocllcab"
the program needs to print "loab", because each letter should only be printed once.
here's the main part of my program
char *do_stuff(char *s1, char *s2)
{
int i, j, k;
char *out;
out = malloc(sizeof(char) * str_len(s1));
i = 0;
j = 0;
k = 0;
while (s2[j] != '\0' && s1[i] != '\0')
{
if (s2[j] == s1[i])
{
if (check_char(out, s1[i]) == 0)
{
out[k] = s1[i];
k++;
}
i++;
j = -1;
}
j++;
}
return (out);
}
my question is: if I dont initialize "out" i have a problem.
i initialize it with malloc at the moment, but i am not allowed to use malloc :).
any other way i tried, seems to not work for me (segmentation fault).
So how do i initialize a string without using malloc?
It's probably obvious, but i'm new at this so pls help. Thanks!
You can always pass the output buffer as a parameter
void do_stuff(char *s1, char *s2, char *out /* some large enough char [] */)
{
int i, j, k;
i = 0;
j = 0;
k = 0;
while (s2[j] != '\0' && s1[i] != '\0')
{
if (s2[j] == s1[i])
{
if (check_char(out, s1[i]) == 0)
{
out[k] = s1[i];
k++;
}
i++;
j = -1;
}
j++;
}
}
and in the calling function
char result[SOME_REASONABLE_SIZE] = {0} /* initialize it for the check_char function */;
do_stuff(argv[1], argv[2], result);
you should check that the function recieved the 2 arguments of course.
One more thing, try not to use strlen in the check char function, pass the current string length k to it, that way your program would be more efficient.
Use the fact that the number of characters is constant (and relatively small):
#include <limits.h>
#define CHAR_NUM (1<<CHAR_BIT)
#define FLAG(x) (1<<(x))
void get_common_chars(char* s1,char* s2,char out[CHAR_NUM])
{
int i,n;
int flags[CHAR_NUM] = {0};
for (i=0; s1[i]!=0; i++)
flags[(unsigned char)s1[i]] |= FLAG(1);
for (i=0; s2[i]!=0; i++)
flags[(unsigned char)s2[i]] |= FLAG(2);
n = 0;
for (i=0; i<CHAR_NUM; i++)
if (flags[i] == FLAG(1)|FLAG(2))
out[n++] = (char)i;
out[n] = 0;
}
If you're only interested in non-capital letters, then you can further improve it:
#define MIN_CHAR 'a'
#define MAX_CHAR 'z'
#define CHAR_NUM (MAX_CHAR-MIN_CHAR+1)
#define FLAG(x) (1<<(x))
void get_common_chars(char* s1,char* s2,char out[CHAR_NUM])
{
int i,n;
int flags[CHAR_NUM] = {0};
for (i=0; s1[i]!=0; i++)
if (MIN_CHAR <= s1[i] && s1[i] <= MAX_CHAR)
flags[s1[i]-MIN_CHAR] |= FLAG(1);
for (i=0; s2[i]!=0; i++)
if (MIN_CHAR <= s2[i] && s2[i] <= MAX_CHAR)
flags[s2[i]-MIN_CHAR] |= FLAG(1);
n = 0;
for (i=0; i<CHAR_NUM; i++)
if (flags[i] == FLAG(1)|FLAG(2))
out[n++] = (char)(MIN_CHAR+i);
out[n] = 0;
}
Here is a usage example:
#include <stdio.h>
int main(int argc,char* argv[])
{
char common_chars[CHAR_NUM];
if (argc >= 3)
{
get_common_chars(argv[1],argv[2],common_chars);
printf("%s\n",common_chars);
}
return 0;
}
If I understand correctly what you need, you should not create a new string, but use the command-line parameters, which are available in the arguments of main().
When you write
int main(int argc, char** argv) {
The compiler will arrange so that argc is the number of command-line arguments, and argv is an array of strings with the arguments. The first, argv[0], is the program name, and the rest are arguments passed to the program.
So this is one way to get your assignment done (high-level description only -- the rest is yours!)
Take the first argument, argv[1], and loop over it, character by character. For each character, try to find it in the other argument, argv[2]. If you find it, print the single character.
No need to allocate memory at all!
edit: if you don't want to print doubles, then one way would be to keep a static array that you could use as an index of already printed characters:
static int printed[26] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
When you print c, set its position to 1. And only print if the character's position is zero.
It's up to you to find out how to find the index of an arbitrary character (and to decide wether you want to differentiate between upper and lower case).

Pushing characters in char array to index 0

I'm trying to build a string from f, being split at whitespace and read it into a struct.
f is the char array I'm iterating over.
I then copy the contents from tmp into ra1.callsign, and essentially empty the tmp char array.
What I want to do is have the the tmp variable start building from index 0 again, so that when I try to strcpy the second time round all the characters in tmp start from index 0.
The way I have it now, when it tries the line: strcpy(ra1.location, tmp) it doesn't copy anything, I think this is because at that point the first character in tmp doesn't appear until some time down the array.
char c;
char tmp[1000];
for (i = 0; i < len; ++i) {
c = f[i];
if (c != ' ') {
tmp[i] = c; //build string to be added
}
//add string to data structure
if (c == ' ') {
if (addTo == CALLSIGN) {
strncpy(ra1.callsign, tmp, strlen(tmp));
memset(tmp, '\0', strlen(tmp));
}
if (addTo == LOCATION) {
strcpy(ra1.location, tmp);
}
++addTo;
}
}
Hope this is clear enough, thanks.
You left out quite a few details in your code and I have made a number of assumptions.
So, using the assumptions that I have made (which you can see in the code below), I believe that this will do what you are trying to accomplish. There are much easier and cleaner ways to do this, but I am hoping that you can get a clear understanding of how it would work with your code.
I have basically added a terminating null character where it is required so the strlen() function will work correctly and utilized an extra variable called cur_size which can be used as an offset based on the current index i.
#include <string.h>
#include <stdio.h>
#define CALLSIGN 3U
#define LOCATION 5U
#define ARRAY_SIZE 50U
typedef struct
{
char callsign[ARRAY_SIZE];
char location[ARRAY_SIZE];
} MyStruct;
MyStruct ra1 = { .callsign = {0}, .location = {0} };
char f[] = "This is my character array. Let's see what happens.";
int main (void)
{
char c;
char tmp[ARRAY_SIZE];
unsigned char addTo = 0;
unsigned char i;
unsigned char cur_size = 0;
for(i = 0; i < sizeof(f); ++i)
{
c = f[i];
if(c != ' ')
{
tmp[i - cur_size] = c; //build string to be added
}
//add string to data structure
if(c == ' ')
{
tmp[i - cur_size] = '\0'; /* YOU NEED THIS FOR strlen(tmp) to work */
cur_size = i + 1;
if(addTo == CALLSIGN)
{
strncpy(ra1.callsign, tmp, strlen(tmp));
//memset(tmp, '\0', strlen(tmp));
}
else if (addTo == LOCATION)
{
strncpy(ra1.location, tmp, strlen(tmp));
}
++addTo;
}
}
for (i = 0; i < ARRAY_SIZE; i++)
{
printf("%c", ra1.callsign[i]);
}
printf("\r\n");
for (i = 0; i < ARRAY_SIZE; i++)
{
printf("%c", ra1.location[i]);
}
printf("\r\n");
return 0;
}

Return Array of Strings with String Input

I'm trying to take a string and break it into "word" components and store that in an array of strings.
"Hello my name is Bill." should give back a char** with elements, "Hello", "my", "name", "is", and "Bill."
My code will compile however I keep encountering a runtime error (I don't get warnings anymore and my debugger gdb doesn't work)>
I'm running on minGW on Window 8.
#include <stdio.h>
#include <stdlib.h>
char** words(char* string)
{
int i = 0;
int j = 0;
int k =0;
int count = 0;
char** stringArray = (char**) malloc(sizeof(char)*30*30);
while( string[i] != '\0' )
{
if(string[i] != ' ')
{
j =0;
while(string[i+j+1] != ' ')
{
j++;
}
i = i+j;
for(k=0; k<=j; k++)
{
stringArray[count][k] = string[i+k];
}
count++;
}
i++;
}
return stringArray;
}
int main()
{
char message[20] = "abcd efgh ijkl mno";
char** wordArray = words(message);
printf("%c\n\n", wordArray[0][0]);
int i =0;
while(wordArray[i])
{
printf("%s\n", wordArray[i]);
i++;
}
printf("\nThe problem is not with the words function");
return 0;
}
There are couple of issues that have been mentioned in the comments.
The allocation should look something like:
#include <ctype.h> // for isspace()
#define MAXSTRLEN 30 // using a symbolic constant
char **stringArray;
int i, j, k;
stringArray = malloc(sizeof(char*) * MAXSTRLEN); // don't cast from malloc
for (i = 0; i < 30; ++i) {
stringArray[i] = malloc(sizeof(char) * MAXSTRLEN);
}
// TODO error checking: malloc could return NULL
while copying the substrings would look like:
i = 0;
j = 0;
while( string[i] != '\0') // go through the whole string
{
while (string[i] != '\0' && isspace(string[i])) {
i++; // skip whitespaces
}
k = 0;
while (string[i] != '\0' && !isspace(string[i])) { // copy word until whitepace or end of string
stringArray[j][k++] = string[i++];
}
stringArray[j][k] = '\0'; // EOS !!!
j++;
}
and printing (j is number of words actually read):
for (i = 0; i < j/*30*/; ++i) { // (!) how to print
printf("%s\n", stringArray[i]);
}
And, yes strtok would also do the job.
In words() you're assigning values to stringArray as a two-dimensional array, and in main() you're reading values from it as an array of pointers. Those are not the same thing.
So you need to change it so that you're consistently treating it as a 2D array, or so that you're consistently treating it as an array of pointers (char* to be exact). Either will work... see the comments above for elaboration.
This code is all wrong.
char** stringArray = (char**) malloc(sizeof(char)*30*30);
First of all, sizeof(char) is always one, second, you don't need to cast a void. So:
char **stringArray = malloc(30 * 30);
But that doesn't make any sense because it's an array of char *, so you should allocate in terms of that:
char **stringArray = malloc(sizeof(char *) * 30);
Or even better:
char **stringArray = malloc(sizeof(*stringArray) * 30);
So now you have an array with 30 char *, but each of those is not initialized, so you need to do that:
for (i = 0; i < 30; i++)
stringArray[i] = malloc(sizeof(**stringArray) * 30);
If you don't do that, you can't access stringArray[count][k].
And then you assume the last element in the array is NULL, but you never set it, so you either do stringArray[count] = NULL at the end of words(), or you do calloc() instead of malloc().
I'm not analyzing the code beyond that; it's just all wrong.

Splitting strings in C and saving it inside an array of strings, but results as garbage

Been searching through here but please forgive me if this post is a duplicate...
I just want to clarify things wherein I am not sure of...
First of all, let me give an introduction to what I have in mind:
The user will be asked to input a command (similar to the command line/shell commands).
The command will be received/inputted as a string separated by white spaces.
Next the string will be tokenized (split), the number of words will be counted.
With that number of words at hand, I will have to create a dynamic multi-dimensional array of strings, where the size/number of strings are the number of words we counted.
Example:
I Name 1234123 123123
The number of words/strings inside that command is 4, so the number of string pointers will be dynamically allocated.
I want this strings to be stored in a dynamic multi-dimensional array where:
char ** arguments;
int count;
I want to access the values inside the arguments array as:
for(i = 0; i < count; i++)
printf("%s", arguments[i]);
MY PROBLEM IS
When I try to access these variables in my main function, the values inside the arguments variable are garbage and the count variable continues to increment, knowing that I have initialized the count variable back to 0.
You can check on my code if I have done something vague or wrong.
Thanks!
Here are the codes that I was able to create where in which I think are useful:
/* This function counts the number of words inside the command input. */
int word_count(char * str)
{
int i, ctr;
for(ctr = i = 0; str[i] != '\0'; i++)
{
while(isspace(str[i]))
i++;
if(str[i] != '\0')
{
ctr++;
while(str[i] != '\0' && ! isspace(str[i]))
i++;
}
}
return ctr;
}
/* This function splits the strings inside the command. */
void split_command(int count, char *** argv, char * command)
{
char buffer[31];
int i, j, k;
*argv = (char **) malloc(sizeof(char *) * count);
for(i = k = 0; command[i] != '\0'; i++)
{
while(isspace(command[i]))
i++;
if(command[i] != '\0')
{
for(j = 0 ;j < 30 && command[i] != '\0' && ! isspace(command[i]); j++, i++)
buffer[j] = (j != 0) ? command[i] : toupper(command[i]);
buffer[j] = '\0';
if(strlen(buffer) > 0)
{
(*argv)[k] = (char *) malloc(sizeof(char) * (strlen(buffer) + 1));
strcpy((*argv)[k], buffer);
k++;
}
}
}
}
/* This function will re-initialize the provided arguments and counters. */
void prepare_command(int * count, char *** argv)
{
int i;
for(i = 0; i < *count; i++)
{
(*argv)[i] = NULL;
free((*argv)[i]);
}
*count = 0;
free(*argv);
*argv = NULL;
}
Main
void main(void)
{
char ** arguments, * command;
int count, i;
boolean end = False; // Assume I created an enum for boolean {False, True}
/* Some initialization here. */
count = 0;
arguments = NULL;
do
{
gets(command);
count = word_count(command); /* This will return the number of strings inside the command */
split_command(count, &arguments, command); /* This will allocate an array. */
/* When I try to display something from here, some values are garbage and some are from the previous allocation... */
for(i = 0; i < count; i++)
printf("%s\n", arguments[i]);
prepare_command(&count, &arguments);
}while(!end);
}
PS: I know strtok and I just don't want to use it, and I don't have strtok_r inside any of my libraries here. So I created my own function to take care of something similar to that.
This might be a long post, but I would like you to help me guys... Thanks!
Will have to edit this post if necessary.
Thank you, hope you shed some light to me. :3 :)
Here is a code snippet that I am using:
int parse_args(char** argv, char* data) {
char c;
int argc = 0;
if (argv)
*argv = data;
int quoteopen = 0;
int waitingnextarg = 1;
for (;(c = *data); data++) {
switch(c) {
case '\n':
case '\t':
case ' ':
if (!quoteopen) {
if (argv)
*data = 0;
waitingnextarg = 1;
}
break;
case '"':
if (argv)
*data = 0;
if (quoteopen) {
waitingnextarg = 1;
}
quoteopen ^= 1;
break;
default:
if (waitingnextarg) {
waitingnextarg = 0;
if (argv)
argv[argc] = data;
argc++;
}
break;
}
}
return argc;
}
call with
int argc = parse_args(0, input);
char* argv[argc+1];
parse_args(argv, input);
Careful: changes the input string. This does not deal with any memory allocations, but only uses preallocated memory. If called with argv == NULL it only counts and returns argc, if called with a valid pointer argv it will change the input string and populate argv.
If you need to preserve a copy of the input string call with
int argc = parse_args(0, input);
char input_copy[strlen(input) +1];
memcpy(input_copy, input, strlen(input) +1);
char* argv[argc+1];
parse_args(argv, input_copy);

Resources