So I want to write a program which will reverse a string taken from the user.
Here's my source code:
#include <stdio.h>
#include <stdlib.h>
int main(int argv, char *argc[]) {
if (argv != 2) {
printf("Please enter the number of elements in your string!\n");
return 1;
}
int n = atoi(argc[1]);
char *c = malloc((sizeof(char) * n) + 1);
char *o = malloc((sizeof(char) * n) + 1);
printf("Enter your string - ");
fgets(c, n, stdin);
for (int i = 0; i < n + 1; i++) {
*(o + i) = *(c + (n - 1) - i);
}
printf("%s\n", o);
free(c);
free(o);
}
But the printed output is nothing!
Can someone please point out what's wrong with my code?
The issue that prevents the code from working is the missmatch in the size of o and c containers, and the read size in fgets, since fgets null-terminates the string read from input.
So let's say n = 6 as you read your string, fgets replaces the 6th character with a null-terminator, when you reverse it the null-terminator will now be the first character in o, essentially, it will be an empty string, as a string is a null-terminated char-array, or byte-array.
To fix this give fgets the size of your mallocced space.
fgets(c, n + 1, stdin);
And null-terminate o when you are finished reversing.
*(o + n) = '\0';
Or
o[n] = '\0'; //you can use this notation which is more readable than dereferencing
Minor issues:
The fact that you switch the names of main arguments. It normally is int main(int argc, char * argv[]). That can be confusing for someone who reads your code.
char *c = malloc((sizeof(char) * n) + 1); has unnecessary logic, it can be char *c = malloc(n + 1);, a char is one byte in size.
There is an underlying problem with the logic of the program, when the inputed string is shorter than what you ask the user the outupt will not be the desired one, you can make an extra effort bullet-proofing your code for erroneous inputs.
All things considered, taking your code as base, it can be something like:
//Only the changed parts are represented, the rest is the same
#include <string.h> //for strlen
//...
if (argc != 2 || atoi(argv[1]) < 1) { //n must be positive (I switched argv and argc)
printf("Please enter the number of elements in your string!\n");
return 1;
}
size_t n = atoi(argv[1]); //size_t type more suited for sizes
char *c = malloc(n + 1);
char *o = malloc(n + 1);
//...
fgets(c, n + 1, stdin); //as stated n + 1 size argument
if(strlen(c) < n) { //if the length of inputed string is shorter than intended
puts("The string size shorter than stated!");
return 1;
}
//...
for (size_t i = 0; i < n + 1; i++) { //repalced int iterator type whith size_t
//...
o[n] = '\0'; //null terminate o
//...
There are multiple problems in your program:
why do you require an argument for the number of characters? it would be much simpler to assume a maximum length and define char arrays in main() with automatic storage.
the statement char *c = malloc((sizeof(char) * n) + 1); computes the correct allocation size, but by chance because sizeof(char) is always 1. You should write char *c = malloc(n + 1); or char *c = malloc(sizeof(*c) * (n + 1));.
since fgets() will store the newline, you should increase the allocation size by 1 to avoid leaving the newline in the input stream, but you will need to avoid including the newline in the characters to reverse. In all cases, you must pass the size of the array to fgets(), not n because fgets() would then only store n - 1 bytes into the array and set c[n - 1] to a null terminator, which causes the reversed string to start with a null terminator, making it an empty string.
you do not test if fgets() succeeded at reading standard input.
you do not compute the number of characters to reverse. If the user entered fewer characters than n, you will transpose bytes beyond those that were entered, possibly null bytes which will make the reversed string empty (this is a good explanation for what you observe).
the transposition loop should iterate for i = 0 while i < n, not n + 1.
you do not set the null terminator at the end of the destination array. This array is allocated with malloc(), so it is uninitialized.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argv, char *argc[]) {
if (argv != 2) {
printf("Please enter the maximum number of characters in your string!\n");
return 1;
}
int n = atoi(argc[1]);
if (n < 1) {
printf("Invalid number of characters: %d\n", n);
return 1;
}
// allocate 1 extra byte for the newline, one more for the null terminator
char *buf = malloc(n + 2);
char *out = malloc(n + 2);
printf("Enter your string: ");
if (!fgets(buf, n + 2, stdin)) {
printf("no input\n");
return 1;
}
// get the number of characters in the input before the newline, if any
int len;
for (len = 0; buf[len] && buf[len != '\n'; n++)
continue;
// if you can use the library function `strcspn()`, replace the for loop with this:
//len = strcspn(buf, "\n");
// copy the string in reverse order
for (int i = 0; i < len; i++) {
out[i] = buf[len - 1 - i];
}
// set the null terminator
out[len] = '\0';
printf("%s\n", out);
free(buf);
free(out);
return 0;
}
It is also possible that you run your program from the IDE on a system that closes the terminal window as soon as the program terminates. This would prevent you from seeing the output. Add a getchar(); before the return 0; to fix this problem or run the program manually from a shell window.
what's wrong with my code!
Key functional problems include:
To read "12345\n" with fgets() takes at least 6 bytes, 7 better1.
Missing null character on o[]
With fgets(c, n, stdin), c[n-1] is a null character and with "reversing", as code assumes n characters, c[n-1] becomes o[0] and so code prints the empty string.
// fgets(c, n, stdin); // too small
fgets(c, n + 1, stdin);
// add before printing.
o[n] = '\0';
Other minor issues exist.
1 Increase allocation too and then lop off \n from input.
your program will fail if the value n does not match the number of
characters in the input string mainly because you do not initialize
the memory that you allocate.
e.g.
n = 10
c = "hello"
length of c is 5 but you have allocated 11 bytes, so the bytes after hello\n\0 are uninitialized in c since fgets will not fill those out for you.
in memory it looks something like this
+---+---+---+---+---+---+---+---+---+---+---+
c ->| h | e | l | l | o |\n |\0 | | | | |
+---+---+---+---+---+---+---+---+---+---+---+
when you turn the string around with
*(o + i) = *(c + n - 1 - i)
since you are using n as offset to start copying characters, you start beyond "hello\n\0" copying
position 9 (10 - 1 - 0) and placing this as first character in o,
but since all of c is not initialized anything can be there, also even a \0 which could explain why you
don't print anything.
better is to once you read the string calculate the length of the string with a simple for loop
int len = 0;
for (len = 0; c[len] && c[len] != '\n'; ++len);
and then use len as the offset instead of n
*(o + i) = *(c + len - 1 + i)
Related
I have this code here that correctly formats the hard-coded sentence and finds the frequency of which a certain letter shows up in that string:
#include <stdio.h>
#include <string.h>
int main() {
char words[1000][100];
int x = 0, y;
char myString[10000] = "The quick Brown ? Fox ? jumps over the Lazy Dog and the !##! LAZY DOG is still sleeping";
printf("Original Text:\n");
printf("%s\n", myString);
// Function for uppercase letters to become lowercase and to remove special characters
for (x = 0; x <= strlen(myString); ++x) {
if (myString[x] >= 65 && myString[x] <= 90)
myString[x] = myString[x] + 32;
}
for (x = 0; myString[x] != '\0'; ++x) {
while (!(myString[x] >= 'a' && myString[x] <= 'z') &&
!(myString[x] >= 'A' && myString[x] <= 'Z') &&
!(myString[x] >= '0' && myString[x] <= '9') &&
!(myString[x] == '\0') && !(myString[x] == ' ')) {
for (y = x; myString[y] != '\0'; ++y) {
myString[y] = myString[y + 1];
}
myString[y] = '\0';
}
}
printf("\nModified Text: \n%s\n", myString);
// Part A
int counts[26] = { 0 };
int k;
size_t myString_length = strlen(myString);
for (k = 0; k < myString_length; k++) {
char c = myString[k];
if (!isalpha(c))
continue;
counts[(int)(c - 'a')]++;
}
printf("\nLetter\tCount\n------ -----\n");
for (k = 0; k < 26; ++k) {
printf("%c\t%d\n", k + 'a', counts[k]);
}
// Part B
int i = 0, count = 0, occurrences[10000] = { 0 };
while (myString[i] != '\0') {
char wordArray[100];
int j = 0;
while (myString[i] != ' ' && myString[i] != '\0') {
wordArray[j++] = myString[i++];
}
if (wordArray[j - 1] == ',' || wordArray[j - 1] == '.') {
wordArray[j - 1] = '\0';
}
wordArray[j] = '\0';
int status = -1;
for (j = 0; j < count; ++j) {
if (strcmp(words[j], wordArray) == 0) {
status = j;
break;
}
}
if (status != -1) {
occurrences[status] += 1;
} else {
occurrences[count] += 1;
strcpy(words[count++], wordArray);
}
++i;
}
printf("\nWord Length\tOccurrences\n----------- -----------\n");
for (i = 0; i < count; ++i) {
// print each word and its occurrences
printf("%s\t\t%d\n", words[i], occurrences[i]);
}
}
Part B is where I'm having a problem though, I want the code to be able to tell me the occurrence of which a word of a specific length shows up, such as this instance:
Word length Occurrences
1 0
2 1
Here, there are no instances where there is a word with one character, but there is one instance where there is a word with two characters. However, my code is outputting the number of times a specific word is given and not what I want above, like this:
Word Length Occurrences
----------- -----------
the 3
quick 1
brown 1
3
fox 1
jumps 1
over 1
lazy 2
dog 2
and 1
is 1
still 1
sleeping 1
How would I go about changing it so that it shows the output I want with just the word length and frequency?
Here are some remarks about your code:
the first loop recomputes the length of the string for each iteration: for (x = 0; x <= strlen(myString); ++x). Since you modify the string inside the loop, it is difficult for the compiler to ascertain that the string length does not change, so a classic optimisation may not work. Use the same test as for the next loop:
for (x = 0; myString[x] != '\0'; ++x)
the test for uppercase is not very readable because you hardcode the ASCII values of the letters A and Z, you should either write:
if (myString[x] >= 'A' && myString[x] <= 'Z')
myString[x] += 'a' - 'A';
or use macros from <ctype.h>:
unsigned char c = myString[x];
if (isupper(c))
myString[x] = tolower(c);
or equivalently and possibly more efficiently:
myString[x] = tolower((unsigned char)myString[x]);
in the second loop, you remove characters that are neither letters, digits nor spaces. You have a redundant nested while loop and a third nested loop to shift the rest of the array for each byte removed: this method has cubic time complexity, O(N3), very inefficient. You should instead use a two finger method that operates in linear time:
for (x = y = 0; myString[x] != '\0'; ++x) {
unsigned char c = myString[x];
if (!isalnum(c) && c != ' ') {
myString[y++] = c;
}
}
myString[y] = '\0';
note that this loop removes all punctuation instead of replacing it with spaces: this might glue words together such as "a fine,good man" -> "a finegood man"
In the third loop, you use a char value c as an argument for isalpha(c). You should include <ctype.h> to use any function declared in this header file. Functions and macros from <ctype.h> are only defined for all values of the type unsigned char and the special negative value EOF. If type char is signed on your platform, isalpha(c) would have undefined behavior if the string has negative characters. In your particular case, you filtered characters that are not ASCII letters, digits or space, so this should not be a problem, yet it is a good habit to always use unsigned char for the character argument to isalpha() and equivalent functions.
Note also that this counting phase could have been combined into the previous loops.
to count the occurrences of words, the array occurrences should have the same number of elements as the words array, 1000. You do not check for boundaries so you have undefined behavior if there are more than 1000 different words and/or if any of these words has 100 characters or more.
in the next loop, you extract words from the string, incrementing i inside the nested loop body. You also increment i at the end of the outer loop, hence skipping the final null terminator. The test while (myString[i] != '\0') will test bytes beyond the end of the string, which is incorrect and potential undefined behavior.
to avoid counting empty words in this loop, you should skip sequences of spaces before copying the word if not at the end of the string.
According to the question, counting individual words is not what Part B is expected to do, you should instead count the frequency of word lengths. You can do this in the first loop by keeping track of the length of the current word and incrementing the array of word length frequencies when you find a separator.
Note that modifying the string is not necessary to count letter frequencies or word length occurrences.
Writing a separate function for each task is recommended.
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#define MAX_LENGTH 100
// Function to lowercase letters and remove special characters
void clean_string(char *str) {
int x, y;
printf("Original Text:\n");
printf("%s\n", str);
for (x = y = 0; str[x] != '\0'; x++) {
unsigned char c = str[x];
c = tolower(c);
if (isalnum(c) || c == ' ') {
str[y++] = c;
}
}
str[y] = '\0';
printf("\nModified Text:\n%s\n", str);
}
// Part A: count letter frequencies
void count_letters(const char *str) {
int letter_count['z' - 'a' + 1] = { 0 };
for (int i = 0; str[i] != '\0'; i++) {
unsigned char c = str[i];
if (c >= 'a' && c <= 'z') {
letter_count[c - 'a'] += 1;
} else
if (c >= 'A' && c <= 'Z') {
letter_count[c - 'A'] += 1;
}
}
printf("\nLetter\tCount"
"\n------\t-----\n");
for (int c = 'a'; c <= 'z'; c++) {
printf("%c\t%d\n", c, letter_count[c - 'a']);
}
}
// Part B: count word lengths frequencies
void count_word_lengths(const char *str) {
int length_count[MAX_LENGTH + 1] = { 0 };
for (int i = 0, len = -1;; i++) {
unsigned char c = str[i];
// counting words as sequences of letters or digits
if (isalnum(c)) {
len++;
} else {
if (len >= 0 && len <= MAX_LENGTH) {
length_count[len] += 1;
len = -1;
}
}
if (c == '\0')
break;
}
printf("\nWord Length\tOccurrences"
"\n-----------\t-----------\n");
for (int len = 0; len <= MAX_LENGTH; len++) {
if (length_count[len]) {
printf("%-11d\t%d\n", len, length_count[len]);
}
}
}
int main() {
char myString[] = "The quick Brown ? Fox ? jumps over the Lazy Dog and the !##! LAZY DOG is still sleeping";
// Uncomment if modifying the string is required
//clean_string(myString);
count_letters(myString);
count_word_lengths(myString);
return 0;
}
Output:
Letter Count
------ -----
a 3
b 1
c 1
d 3
e 6
f 1
g 3
h 3
i 4
j 1
k 1
l 5
m 1
n 3
o 5
p 2
q 1
r 2
s 4
t 4
u 2
v 1
w 1
x 1
y 2
z 2
Word Length Occurrences
----------- -----------
1 1
2 7
3 3
4 4
7 1
Use strtok_r() and simplify counting.
It's sibling strtok() is not thread-safe. Discussed in detail in Why is strtok() Considered Unsafe?
Also, strtok_r() chops input string by inserting \0 chars inside the string. If you want to keep a copy of original string, you have to make a copy of original string and pass it on to strtok_r().
There is also another catch. strtok_r() is not a part of C-Standard yet, but POSIX-2008 lists it. GNU glibc implements it, but to access this function we need to #define _POSIX_C_SOURCE before any includes in our source files.
There is also strdup() & strndup() which duplicate an input string, they allocate memory for you. You've to free that string-memory when you're done using it. strndup() was added in POSIX-2008 so we declare 200809L in our sources to use it.
It's always better to use new standards to write fresh code. POSIX 200809L is recommended with at least C standard 2011.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_STR_LEN 1024
#define MAX_WORD_LEN 128
#define WORD_DELIMS " \n\t"
int is_word (const char* str, const size_t slen) {
int word = 0;
for (size_t ci = 0; ci < slen;)
if (isalnum (str[ci++])) {
word = 1;
break;
}
return word;
}
void get_word_stat (const char* str, int word_stat[]) {
char *copy = strndup (str, MAX_STR_LEN); // limiting copy
if (!copy) { // copying failed
printf ("Error duplicating input string\n");
exit (1);
}
for (char *token, *rmdStr = copy; (token = strtok_r (NULL, WORD_DELIMS, &rmdStr)); /* empty */) {
size_t token_len = strlen (token);
if (token_len > (MAX_WORD_LEN - 1)) {
printf ("Error: Increase MAX_WORD_LEN(%d) to handle words of length %lu\n", MAX_WORD_LEN, token_len);
exit (2);
}
if (is_word (token, token_len))
++word_stat[token_len];
else
printf ("[%s] not a word\n", token);
}
free (copy);
}
int main () {
char str [MAX_STR_LEN] = "The quick Brown ? Fox ? jumps over the Lazy Dog and the !##! LAZY DOG is still sleeping";
printf ("Original Text: [%s]\n", str);
int word_stat[MAX_WORD_LEN] = {0};
get_word_stat (str, word_stat);
printf ("\nWordLength Occurrences\n");
for (int si = 1; si < MAX_WORD_LEN; ++si) {
if (word_stat[si])
printf ("%d\t\t%d\n", si, word_stat[si]);
}
return 0;
}
Whenever you are interested in the frequency that something occurs, you want to use a Frequency Array containing the number of elements necessary to handle the entire range of possible occurrence. You want to track the frequency of word-lengths, so you need an array that is sized to track the longest word. (longest word in the non-medical unabridged dictionary is 29-characters, longest medical word is 45-characters)
So here a simple array of integers with 29 elements will do (unless you want to consider medical words, then use 45). If you want to consider non-sense words, then size appropriately, e.g. "Supercalifragilisticexpialidocious", 34-characters. Chose the type based on a reasonably anticipated maximum number of occurrences. Using signed int that limits the occurrences to INT_MAX (2147483647). Using unsigned will double the limit, or using uint64_t for a full 64-bit range.
How it works
How do you use a simple array to tract the occurrences of word lengths? Simple, declare an array of sufficient size and initialize all elements zero. Now all you do is read a word, use, e.g. size_t len = strlen(word); to get the length and then increment yourarray[len] += 1;.
Say the word has 10-characters, you will add one to yourarray[10]. So the array index corresponds word-length. When you have taken the length of all words and incremented the corresponding array index, to get your results, you just loop over your array and output the value (number of occurrences) at the index (word-length). If you have had two words that were 10-characters each, then yourarray[10] will contain 2 (and so on and so forth for every other index that corresponds to a different word-length number of characters).
Consideration When Choosing How to Separate Words
When selecting a method to split a string of space separated words into individual words, you need to know whether your original string is mutable. For example, if you choose to separate words with strtok(), it will modify the original string. In your case since your words are stored in an array or char, that is fine, but what if you had a string-literal like:
char *mystring = "The quick Brown ? Fox ? jumps over the Lazy Dog ";
In that case, passing mystring to strtok() would SEGFAULT when strtok() attempts to modify the region of read-only memory holding mystring (ignoring the non-standard treatment of string-literals by Microsoft)
You can of course make a copy of mystring and put the string-literal in mutable memory and then call strtok() on the copy. Or, you can use a method that does not modify mystring (like using sscanf() and an offset to parse the words, or using alternating calls to strcspn() and strspn() to locate and skip whitespace, or simply using a start and end pointer to work down the string bracketing words and copying characters between the pointers. Entirely up to you.
For example, using sscanf() with an offset to work down the string, updating the offset from the beginning with the number of characters consumed during each read you could do:
char *mystring = "The quick Brown ? Fox ? jumps over the Lazy Dog "
"and the !##! LAZY DOG is still sleeping",
*p = mystring, /* pointer to mystring to parse */
buf[MAXLEN] = ""; /* temporary buffer to hold each word */
int nchar = 0, /* characters consumed by sscanf */
offset = 0; /* offset from beginning of mystring */
/* loop over each word in mystring using sscanf and offset */
while (sscanf (p + offset, "%s%n", buf, &nchar) == 1) {
size_t len = strlen (buf); /* length of word */
offset += nchar; /* update offset with nchar */
/* do other stuff here */
}
Testing if Words is Alphanum
You can loop over each character calling the isalnum() macro from ctype.h on each character. Or, you can let strspn() do it for you given a list of characters that your words can contain. For example for digits and alpha-characters only, you can use a simple constant, and then call strspn() in your loop to determine if the word is made up only of the characters you will accept in a word, e.g.
#define ACCEPT "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
...
/* use strspn to test that word is valid (alphanum) or get next word */
if (strspn (buf, ACCEPT) != len) {
fprintf (stderr, " error: rejecting \"%s\"\n", buf); /* optional */
continue;
}
...
Neither way is more-right than the other, it's really a matter of convenience and readability. Using a library provided function also provides a bit of confidence that it is written in a manner that will allow the compiler to fully optimize the compiled code.
A Short Example
Putting the thoughts above together in a short example that will parse the words in mystring using sscanf() and then track the occurrences of all alphanum words (up to 31-characters, and outputting any word rejected) using a simple array of integers to hold the frequency of length, you could do:
#include <stdio.h>
#include <string.h>
#define MAXLEN 32 /* if you need a constant, #define one (or more) */
#define ACCEPT "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
int main (void) {
char *mystring = "The quick Brown ? Fox ? jumps over the Lazy Dog "
"and the !##! LAZY DOG is still sleeping",
*p = mystring, /* pointer to mystring to parse */
buf[MAXLEN] = ""; /* temporary buffer to hold each word */
int nchar = 0, /* characters consumed by sscanf */
offset = 0, /* offset from beginning of mystring */
lenfreq[MAXLEN] = {0}; /* frequency array for word length */
/* loop over each word in mystring using sscanf and offset */
while (sscanf (p + offset, "%s%n", buf, &nchar) == 1) {
size_t len = strlen (buf); /* length of word */
offset += nchar; /* update offset with nchar */
/* use strspn to test that word is valid (alphanum) or get next word */
if (strspn (buf, ACCEPT) != len) {
fprintf (stderr, " error: rejecting \"%s\"\n", buf); /* optional */
continue;
}
lenfreq[len] += 1; /* update frequency array of lengths */
}
/* output original string */
printf ("\nOriginal Text:\n\n%s\n\n", mystring);
/* output length frequency array */
puts ("word length Occurrences\n"
"----------- -----------");
for (size_t i = 0; i < MAXLEN; i++) {
if (lenfreq[i])
printf ("%2zu%14s%d\n", i, " ", lenfreq[i]);
}
}
Example Use/Output
Compiling and running the program would produce:
$ ./bin/wordlen-freq
error: rejecting "?"
error: rejecting "?"
error: rejecting "!##!"
Original Text:
The quick Brown ? Fox ? jumps over the Lazy Dog and the !##! LAZY DOG is still sleeping
word length Occurrences
----------- -----------
2 1
3 7
4 3
5 4
8 1
(note: you can output all lengths from 0 to 31 even if there were no occurrences by removing the print condition if (lenfreq[i]) -- up to you)
Look things over and let me know if you have questions.
I'm trying to add an additional letter if there are two equal letters beside each other.
That's what I was thinking, but it doesn't put in an x between the two letters; instead of that, it copies one of the double letters, and now I have, for example, MMM instead of MXM.
for (index_X = 0; new_text[index_X] != '\0'; index_X++)
{
if (new_text[index_X] == new_text[index_X - 1])
{
double_falg = 1;
}
text[index_X] = new_text[index_X];
}
if (double_falg == 1)
{
for (counter_X = 0; text[counter_X] != '\0'; counter_X++)
{
transfer_X = counter_X;
if (text[transfer_X - 1] == text[transfer_X])
{
text_X[transfer_X] = 'X';
cnt_double++;
printf("%c\n", text[transfer_X]);
}
text_X[transfer_X] = text[transfer_X - cnt_double];
}
printf("%s\n", text_X);
}
If you're trying to create the modified array in text_X, copying data from new_text and putting an X between adjacent repeated letters (ignoring the possibility that the input contains XX), then you only need:
char new_text[] = "data with appalling repeats";
char text_X[SOME_SIZE];
int out_pos = 0;
for (int i = 0; new_text[i] != '\0'; i++)
{
text_X[out_pos++] = new_text[i];
if (new_text[i] == new_text[i+1])
text_X[out_pos++] = 'X';
}
text_X[out_pos] = '\0';
printf("Input: [%s]\n", new_text);
printf("Output: [%s]\n", text_X);
When wrapped in a basic main() function (and enum { SOME_SIZE = 64 };), that produces:
Input: [data with appalling repeats]
Output: [data with apXpalXling repeats]
To deal with repeated X's in the input, you could use:
text_X[out_pos++] = (new_text[i] == 'X') ? 'Q' : 'X';
It seems that your approach is more complicated than needed - too many loops and too many arrays involved. A single loop and two arrays should do.
The code below iterates the original string with idx to track position and uses the variable char_added to count how many extra chars that has been added to the new array.
#include <stdio.h>
#define MAX_LEN 20
int main(void) {
char org_arr[MAX_LEN] = "aabbcc";
char new_arr[MAX_LEN] = {0};
int char_added = 0;
int idx = 1;
new_arr[0] = org_arr[0];
if (new_arr[0])
{
while(org_arr[idx])
{
if (org_arr[idx] == org_arr[idx-1])
{
new_arr[idx + char_added] = '*';
++char_added;
}
new_arr[idx + char_added] = org_arr[idx];
++idx;
}
}
puts(new_arr);
return 0;
}
Output:
a*ab*bc*c
Note: The code isn't fully tested. Also it lacks out-of-bounds checking.
There is a lot left to be desired in your Minimal, Complete, and Verifiable Example (MCVE) (MCVE). However, that said, what you will need to do is fairly straight-forward. Take a simple example:
"ssi"
According to your statement, you need to add a character between the adjacent 's' characters. (you can use whatever you like for the separator, but if your input are normal ASCII character, then you can set the current char to the next ASCII character (or subtract one if current is the last ASCII char '~')) See ASCII Table and Description.
For example, you could use memmove() to shift all characters beginning with the current character up by one and then set the current character to the replacement. You also need to track the current length so you don't write beyond your array bounds.
A simple function could be:
#include <stdio.h>
#include <string.h>
#define MAXC 1024
char *betweenduplicates (char *s)
{
size_t len = strlen(s); /* get length to validate room */
if (!len) /* if empty string, nothing to do */
return s;
for (int i = 1; s[i] && len + 1 < MAXC; i++) /* loop until end, or out of room */
if (s[i-1] == s[i]) { /* adjacent chars equal? */
memmove (s + i + 1, s + i, len - i + 1); /* move current+ up by one */
if (s[i-1] != '~') /* not last ASCII char */
s[i] = s[i-1] + 1; /* set to next ASCII char */
else
s[i] = s[i-1] - 1; /* set to previous ASCII char */
len += 1; /* add one to len */
}
return s; /* convenience return so it can be used immediately if needed */
}
A short example program taking the string to check as the first argument could be:
int main (int argc, char **argv) {
char str[MAXC];
if (argc > 1) /* if argument given */
strcpy (str, argv[1]); /* copy to str */
else
strcpy (str, "mississippi"); /* otherwise use default */
puts (str); /* output original */
puts (betweenduplicates (str)); /* output result */
}
Example Use/Output
$ ./bin/betweenduplicated
mississippi
mistsistsipqpi
or when there is nothing to replace:
$ ./bin/betweenduplicated dog
dog
dog
Or checking the extremes:
$ ./bin/betweenduplicated "two spaces and alligators ~~"
two spaces and alligators ~~
two ! spaces ! and ! almligators ! ~}~
There are a number of ways to approach it. Let me know if you have further questions.
I'm struggling with rearranging my array. I have used from single to multiple loops trying to put spaces (white characters) between two pairs of characters, but I was constantly rewriting the original input. So there is always an input of even length, for example ABCDEFGH. And my task would be to extend the size of the array by putting spaces after every 2 chars (except the last one).
So the output would be:
AB CD EF GH
So the size of output (if I'm correct) will be (2*input_len)-1
Thanks.
EDIT:
This is my code so far
// output = "ABCDEFGHIJKL
char c1;
char c2;
char c3;
int o_len = strlen(output);
for(int i = 2; i < o_len + olen/2; i = i + 3){
if(i == 2){
c1 = output[i];
c2 = output[i+1];
c3 = output[i+2];
output[i] = ' ';
output[i+1] = c1;
output[i+2] = c2;
}
else{
c1 = output[i];
c2 = output[i+1];
output[i] = ' ';
output[i+1] = c3;
output[i+2] = c1;
c3 = c2;
}
}
So the first 3 pairs are printed correctly, then it is all a mess.
Presuming you need to store the space separate result, probably the easiest way to go about inserting the spaces is simply to use a pair of pointers (one to your input string and one to your output string) and then just loop continually writing a pair to your output string, increment both pointers by 2, check whether you are out of characters in your input string (if so break; and nul-terminate your output string), otherwise write a space to your output string and repeat.
You can do it fairly simply using memcpy (or you can just copy 2-chars to the current pointer and pointer + 1, your choice, but since you already include string.h for strlen() -- make it easy on yourself) You can do something similar to:
#include <stdio.h>
#include <string.h>
#define ARRSZ 128 /* constant for no. of chars in output string */
int main (int argc, char **argv) {
char *instr = argc > 1 ? argv[1] : "ABCDEFGH", /* in string */
outstr[ARRSZ] = "", /* out string */
*ip = instr, *op = outstr; /* pointers to each */
size_t len = strlen (instr); /* len of instr */
if (len < 4) { /* validate at least 2-pairs worth of input provided */
fputs ("error: less than two-pairs to separate.\n", stderr);
return 1;
}
if (len & 1) { /* validate even number of characters */
fputs ("error: odd number of characters in instr.\n", stderr);
return 1;
}
if (ARRSZ < len + len / 2) { /* validate sufficient storage in outstr */
fputs ("error: insufficient storage in outstr.\n", stderr);
return 1;
}
for (;;) { /* loop continually */
memcpy (op, ip, 2); /* copy pair to op */
ip += 2; /* increment ip by 2 for next pair */
op += 2; /* increment op by 2 for next pair */
if (!*ip) /* check if last pair written */
break;
*op++ = ' '; /* write space between pairs in op */
}
*op = 0; /* nul-terminate outstr */
printf ("instr : %s\noutstr : %s\n", instr, outstr);
}
Example Use/Output
$ ./bin/strspaceseppairs
instr : ABCDEFGH
outstr : AB CD EF GH
$ ./bin/strspaceseppairs ABCDEFGHIJLMNOPQ
instr : ABCDEFGHIJLMNOPQ
outstr : AB CD EF GH IJ LM NO PQ
Odd number of chars:
$ ./bin/strspaceseppairs ABCDEFGHIJLMNOP
error: odd number of characters in instr.
Or short string:
$ ./bin/strspaceseppairs AB
error: less than two-pairs to separate.
Look things over and let me know if you have further questions.
Edit To Simply Output Single-Pair or Empty-String
Based upon the comment by #chqrlie it may make more sense rather than issuing a diagnostic for a short string, just to output it unchanged. Up to you. You can modify the first conditional and move it after the odd character check in that case, e.g.
if (len & 1) { /* validate even number of characters */
fputs ("error: odd number of characters in instr.\n", stderr);
return 1;
}
if (len < 4) { /* validate at least 2-pairs worth of input provided */
puts(instr); /* (otherwise output unchanged and exit) */
return 0;
}
You can decide how you want to handle any aspect of your program and make the changes accordingly.
I think you are looking for a piece of code like the one below:
This function returns the output splitted array, as you requested to save it.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
char* split_by_space(char* str, size_t length, size_t step) {
size_t i = 0, j = 0, spaces = (length / step);
char* splitted = malloc(length + spaces + 1);
for (i = 0, j = 0; i < length; ++i, ++j) {
if (i % step == 0 && i != 0) {
splitted[j] = ' ';
++j;
}
splitted[j] = str[i];
}
splitted[j] = '\0';
return splitted;
}
int main(void) {
// Use size_t instead of int.
size_t step = 2; // Also works with odd numbers.
char str[] = "ABCDEFGH";
char* new_str;
// Works with odd and even steps.
new_str = split_by_space(str, strlen(str), step);
printf("New splitted string is [%s]", new_str);
// Don't forget to clean the memory that the function allocated.
free(new_str);
return 0;
}
When run with a step value of 2, the above code, outputs:
New splitted string is [AB CD EF GH]
Inserting characters inside the array is cumbersome and cannot be done unless you know the array is large enough to accommodate the new string.
You probably want to allocate a new array and create the modified string there.
The length of the new string is not (2 * input_len) - 1, you insert a space every 2 characters, except the last 2: if the string has 2 or fewer characters, its length is unmodified, otherwise it increases by (input_len - 2) / 2. And in case the length is off, you should round this value to the next integer, which is done in integer arithmetics this way: (input_len - 2 + 1) / 2.
Here is an example:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *reformat_with_spaces(const char *str) {
size_t len = strlen(str);
size_t newlen = len > 2 ? len + (len - 2 + 1) / 2 : len;
char *out = malloc(newlen + 1);
if (out) {
for (size_t i = 0, j = 0; i < len; i++) {
if (i > 0 && i % 2 == 0) {
out[j++] = ' ';
}
out[j++] = str[i];
}
out[j] = '\0';
}
return out;
}
int main(void) {
char buf[256];
char *p;
while (fgets(buf, sizeof buf, stdin)) {
buf[strcspn(buf, "\n")] = '\0'; // strip the newline if any
p = reformat_with_spaces(buf);
if (p == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
puts(p);
free(p);
}
return 0;
}
Try this,
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void rearrange(char *str)
{
int len=strlen(str),n=0,i;
char *word=malloc((len+(int)(len/2)));
if(word==NULL)
{
printf("Memory Error");
exit(1);
}
for(i=0;i<len;i++)
{
if( i % 2 == 0 && i != 0)
{
word[n]=' ';
n++;
word[n]=str[i];
n++;
}
else
{
word[n]=str[i];
n++;
}
}
word[n]='\0';
strcpy(str,word);
free(word);
return;
}
int main()
{
char word[40];
printf("Enter word:");
scanf("%s",word);
rearrange(word);
printf("\n%s",word);
return 0;
}
See Below:
The rearrange function saves the letters in str into word. if the current position is divisible by 2 i.e i%2 it saves one space and letter into str, otherwise it saves letter only.
This program, tokenizes a user input string, removes extra spaces and saves each word into a 2D array and then print the tokens
EXAMPLE:
input: " Hello world string house and car"
output and EXPECTED output:
token[0]: Hello
token[1]: world
token[2]: string
token[3]: house
token[4]: and
token[5]: car
THE PROBLEM:
the problem is that I achieved this by using strlen() function when printing the tokens(code located at the very bottom), I am not supposed to use any other library than stdio.h and stdlib.h, since strlen() function is defined in string.h i tried to use sizeof(arr) / sizeof(arr[0]); but it does not work as I want, the result using sizeof is :
token[0]: Hello
token[1]: world
token[2]: string
token[3]: house
token[4]: and
token[5]: car
�oken[6]: ��
token[7]: �
token[8]: ����
token[9]: �
token[10]:
I WOULD LIKE TO HAVE THE EXPECTED OUTPUT WITHOUT USING STRLEN()
#include<stdio.h>
#include <stdlib.h>
#define TRUE 1
char tokenize(char *str, char array[10][20])
{
int n = 0, i, j = 0;
for(i = 0; TRUE; i++)//infinite loop until is the end of the string '\0'
{
if(str[i] != ' '){
//position 1, char 1
array[n][j++] = str[i];// if, it is not space, we save the character
}
else{
array[n][j++] = '\0';//end of the first word
n++;// position for next new word
j=0;// start writting char at position 0
}
if(str[i] == '\0')
break;
}
return 0;
}
//removes extra spaces
char* find_word_start(char* str){
/*also removes all extra spaces*/
char *result = (char*) malloc(sizeof(char) *1000);
int c = 0, d = 0;
// no space at beginning
while(str[c] ==' ') {
c++;
}
while(str[c] != '\0'){ // till end of sentence
result[d++] = str[c++]; //take non-space characters
if(str[c]==' ') { // take one space between words
result[d++] = str[c++];
}
while(str[c]==' ') { //
c++;
}
}
result[d-1] = '\0';
//print or return char?
return result;
free(result);
}
int main()
{
char str[]=" Hello world string dudes and dudas ";
//words, and chars in each word
char arr[10][20];
//call the method to tokenize the string
tokenize(find_word_start(str),arr);
int row = sizeof(arr) / sizeof(arr[0]);
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
for(int i = 0;i <= strlen(arr);i++)
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
printf("token[%d]: %s\n", i, arr[i]);
return 0;
}
Your code using strlen() may appear the work in this instance but it is not correct.
strlen(arr) makes no semantic sense because arr is not a string. It happens in this case to return 5 because arr has the same address as arr[0], then you kludged it to work for the 6 word output by using the test i <= strlen(arr) in the for loop. The two values strlen(arr) and the number of strings stored in arr are not related.
The expression sizeof(arr) / sizeof(arr[0]) determines the run-time constant number arrays within the array of arrays arr (i.e. 10), not the number of valid strings assigned. It is your code's responsibility to keep track of that either with a sentinel value such as an empty string, or by maintaining a count of strings assigned.
I suggest you change tokenize to return the number of strings (currently it is inexplicably defined to return a char, but in fact only ever rather uselessly returns zero):
int tokenize( char* str, char array[][20] )
{
...
return n ;
}
Then:
int rows = tokenize( find_word_start(str), arr ) ;
for( int i = 0; i < rows; i++ )
{
printf( "token[%d]: %s\n", i, arr[i] ) ;
}
I am writing a program which will take every 3 numbers in a file and convert them to their ASCII symbol. So I thought I could read the numbers into a character array, and then make every 3 elements 1 element in a second array, convert them to int and then print these as char.
I am stuck on taking every 3 elements, however. This is my code snippet for this part:
char arry[] = "073102109109112"; <--example string read from a file
char arryNew[16] = {0};
for(int i = 0; i <= sizeof(arryNew); i++){
strncpy(arryNew, arry, 3);
arryNew[i+3]='\0';
puts(arryNew);
}
What this code gives me is the first 3 numbers, fifteen times. I've tried incrementing i by 3, which gives me the first 3 numbers 5 times. How do I write a for-loop with strncpy so that after copying n chars, it moves to the next n chars?
You pass always the pointer to the beginning of the array, so you will always have the same result of course. You must include the loop counter to get at the next block:
strncpy(arryNew, &arry[i*3], 3);
Here you have a problem:
arryNew[i+3]='\0';
First of all, you don't need to set the null byte every time, because this will not change anyway. Additionally you will corrupt memory, because you use i+3 as the index so when you reach 14 and 15, it will write beyond the arrayboundary.
Your arrayNew must be longer, because your original array is 16 characters, and your target array is also. If you intend to have several 3char strings in there, then you must have 5*4 characters for your target, because each string also has the 0-byte.
And of course, you must also use the index here as well. The way it is written now, it will write beyond the array boundary, when i reaches 14 and 15.
So what you seem to want to do (not sure from your description) is:
char arry[] = "073102109109112"; <--example string read from a file
char arryNew[20] = {0};
for(int i = 0; i <= sizeof(arry); i++)
{
strncpy(&arryNew[i*4], &arry[i*3], 3);
puts(&arryNew[i*4]);
}
Or if you just want to have the individual strings printed then you can just do:
char arry[] = "073102109109112"; <--example string read from a file
char arryNew[4] = {0};
for(int i = 0; i <= sizeof(arry); i++)
{
strncpy(arryNew, &arry[i*3], 3);
puts(arryNew);
}
Making things a bit simpler: your target string doesn't change.
char arry[] = "073102109109112"; <--example string read from a file
char target[4] = {0};
for(int i = 0; i < strlen(arry) - 3; i+=3)
{
strncpy(target, arry + i, 3);
puts(target);
}
Decoding:
start at the beginning of arry
copy 3 characters to target
(note the fourth element of target is \0)
print out the contents of target
increment i by 3
repeat until you fall off the end of the string.
Some problems.
// Need to change a 3 chars, as text, into an integer.
arryNew[i] = (char) strtol(buf, &endptr, 10);
// char arryNew[16] = {0};
// Overly large.
arryNew[6]
// for(int i = 0; i <= sizeof(arryNew); i++){
// Indexing too far. Should be `i <= (sizeof(arryNew) - 2)` or ...
for (i=0; i<arryNewLen; i++) {
// strncpy(arryNew, arry, 3);
// strncpy() can be used, but we know the length of source and destination,
// simpler to use memcpy()
// strncpy(buf, a, sizeof buf - 1);
memcpy(buf, arry, N);
// arryNew[i+3]='\0';
// Toward the loop's end, code is writing outside arryNew.
// Lets append the `\0` after the for() loop.
// int i
size_t i; // Better to use size_t (or ssize_t) for array index.
Suggestion:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char Source[] = "073102109109112"; // example string read from a file
const int TIW = 3; // textual integer width
// Avoid sprinkling bare constants about code. Define in 1 place instead.
const char *arry = Source;
size_t arryLen = strlen(arry);
if (arryLen%TIW != 0) return -1; // is it a strange sized arry?
size_t arryNewLen = arryLen/TIW;
char arryNew[arryNewLen + 1];
size_t i;
for (i=0; i<arryNewLen; i++) {
char buf[TIW + 1];
// strncpy(buf, a, sizeof buf - 1);
memcpy(buf, arry, TIW);
buf[TIW] = '\0';
char *endptr; // Useful should OP want to do error checking
// TBD: test if result is 0 to 255
arryNew[i] = (char) strtol(buf, &endptr, 10);
arry += TIW;
}
arryNew[i] = '\0';
puts(arryNew); // prints Ifmmp
return 0;
}
You could use this code to complete your task i.e. to convert the given char array in form of ascii value.
char arry[] = "073102109109112";
char arryNew[16] = {0};
int i,j=0;
for(i = 0; i <= sizeof(arryNew)-2; i+=3)
{
arryNew[j]=arry[i]*100+arry[i+1]*10+arry[i+2]*1;
j++;
arryNew[j+1]='\0';
puts(arryNew);
}