#include <stdio.h>
#include <string.h>
int main(void) {
char string[1024];
int len = 0;
int i = 0;
while (fgets(string, sizeof(string), stdin) != 0);
len = strlen(string) - 1;
if (len % 2 == 0) {
printf("%s", string);
}
}
The aim of this code is to print out inputs that have an even number of characters and omit anything else (will not print it). The program works when there is no space in the string however once I place a space it counts it as the length which I'm trying to stop. How do I make this program omit spaces when counting the length of the string?
How do I make the strlen() function not count spaces?
The standard function strlen() simple does not do that. Easy to code a new function that does.
#include <ctype.h>
#include <stddef.h>
size_t spaceless_strlen(const char *s) {
size_t len = 0;
const unsigned char *us = (const unsigned char *) s;
while (*us) {
if (*us != ' ') len++;
// Or if not counting white-spaces
if (!isspace(*us)) len++;
us++;
}
return len;
}
Best to pass unsigned char values to is...() functions so a unsigned char * pointer was used.
The trick is to only count characters you want. strlen() counts all characters. Write yourself a function:
#include <string.h>
size_t count( const char * s, int (*f)( int ) )
//
// Return the number of elements in the c-string
// `s` that satisfy the predicate function `f()`.
// The function type matches the Standard Library
// character classification functions in <ctype.h>.
{
size_t result = 0;
while (*s) result += f( *s++ );
return result;
}
With that in hand, you need a predicate function that will return 1 for non-whitespace characters, and 0 for whitespace characters. Easy enough:
#include <ctype.h>
int not_isspace( int c )
{
return !isspace( c );
}
Now you can totally count the number of non-whitespace characters in your string:
int length = count( s, ¬_isspace );
That’s it!
Regarding how you can achieve counting characters that are not spaces, you can try this.
#include <stdio.h>
#include <string.h>
int main(void) {
char string[1024];
int len;
int count=0;
int i;
while (fgets(string, sizeof(string), stdin) != 0){
len = strlen(string) ;
for (i=0; i<len;i++)
{
if (string[i]!=' ')
count++;
}
if (count % 2 == 0)
{
printf("%s", string);
}
}
return 0;
}
Note: In my opinion, this isn't the most optimal way to achieve so but I tried to keep it based on your code and logic!
This seems better :
EDIT : it is if you replace the get function with fgets as you initially did!
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[1024];
int i=0,count=0;
printf("Enter a String\n");
gets(str);
while(str[i]!='\0')
{
if(str[i]!=' ')
{
count++;
}
i++;
}
if(count% 2 ==0)
{
printf("%s ",str,);
}
return 0;
}
It is possible to make a dedicated function using fgetc() instead of calling fgets() which returns the read string and its length (without counting the spaces):
#include <stdio.h>
#include <string.h>
#include <ctype.h>
static int my_fgets(char *s, int size, FILE *stream)
{
char c;
int len;
char *in;
if (!s || (size <= 0)) {
return 0;
}
len = 0;
in = s;
do {
c = fgetc(stream);
if ((len < (size - 1)) && (c != EOF) && (c != '\n')) {
if (!isspace(c)) {
len ++;
}
*(in ++) = c;
}
} while ((len < (size - 1)) && (c != EOF) && (c != '\n'));
*in = '\0';
return len;
}
int main(void) {
char string[1024];
int len = 0;
int i = 0;
len = my_fgets(string, sizeof(string), stdin);;
if (len % 2 == 0) {
printf("%s, len=%d\n", string, len);
}
}
If you aren't super-concerned about performance, the easiest to read version might be to call strchr repeatedly in a loop, count the spaces and then subtract them from the string length. Example:
size_t spaceout (const char* original)
{
size_t spaces = 0;
for(char* str=strchr(original,' '); str!=NULL; str=strchr(str+1,' '))
{
spaces++;
}
return strlen(original) - spaces;
}
This only looks for ' ' and it actually iterates over the string twice because of the final strlen call. But the code is quite easy to read.
A possible micro-optimization would be to pass the string length as parameter in case the caller already knows it.
Related
#include <stdio.h>
#include <string.h>
void main(void)
{
char in[15], rev[15];
printf("Enter a word (upto 15 letters): ");
gets(in);
for (int i = 0, j = 15; i < strlen(in); i++, j--)
{
rev[i] = in[j];
}
puts(rev);
}
Shows no error, just not working.
What am I doing wrong?
Edit : no strrev
For starters according to the C Standard the function main without parameters shall be declared like
int main( void )
The function gets is unsafe and is not supported by the C Standard. Instead use either scanf or fgets.
The function strlen is a standard C string function. So according to the requirement you may not use it.
You are not reversing a string. You are trying to copy a string in the reverse order into another string.
The program can look the following way
#include <stdio.h>
int main(void)
{
enum { N = 15 };
char in[N] = "", rev[N];
printf("Enter a word (upto %d letters): ", N - 1 );
scanf( " %14s", in );
size_t n = 0;
while ( in[n] ) ++n;
rev[n] = '\0';
for ( size_t i = 0; i < n; i++ )
{
rev[n - i - 1] = in[i];
}
puts( rev );
}
If you actually need to reverse a string in place then the program can look the following way
#include <stdio.h>
int main(void)
{
enum { N = 15 };
char in[N] = "";
printf("Enter a word (upto %d letters): ", N - 1 );
scanf( " %14s", in );
size_t n = 0;
while ( in[n] ) ++n;
for ( size_t i = 0; i < n / 2; i++ )
{
char c = in[i];
in[i] = in[n - i - 1];
in[n - i - 1] = c;
}
puts( in );
}
EDIT: getline is not standard C, and it is only recognized by POSIX systems. Another solution is to use fgets that works for both OSes. I provided both examples.
As others have already pointed out, you are making some mistakes:
Unsafe practice when getting input from the user.
Always starting from 15 even if the input string has less chars.
I have created a little example with dynamic allocation that works with more than 15 characters and fixes the afore-mentioned issues. Comments inline to key points.
Example: getline - POSIX
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]) {
// Idea from https://stackoverflow.com/questions/7709452/how-to-read-string-from-keyboard-using-c
char *line = NULL; /* forces getline to allocate with malloc */
size_t len = 0; /* ignored when line = NULL */
ssize_t read;
read = getline(&line, &len, stdin);
if (read > 0)
{
printf ("\n String from user: %s\n", line);
}else
{
printf ("Nothing read.. \n");
return -1;
}
// Now we need the same amount of byte to hold the reversed string
char* rev_line = (char*)malloc(read);
// "read-1" because we start counting from 0.
for (int i = 0, j = read-1; i < read; i++, j--)
{
rev_line[i] = line[j];
}
printf("%s\n",rev_line);
free (line); /* free memory allocated by getline */
free(rev_line);
return 0;
}
Example: fgets - C standard
fgets does not return the number of characters read, so it has to be chained with strlen to decide how many characters to allocate for the reversed string.
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
int main (int argc, char *argv[]) {
char line[LINE_MAX];
size_t len = 0; /* ignored when line = NULL */
ssize_t read;
if (fgets(line, LINE_MAX, stdin) != NULL)
{
line[strcspn(line, "\n")] = '\0'; //fgets() reads the \n character (that's when you press Enter).
read = strlen(line);
printf ("\n String from user: %s\n", line);
}else
{
printf ("Nothing read.. \n");
return -1;
}
// Now we need the same amount of byte to hold the reversed string
char* rev_line = (char*)malloc(read);
for (int i = 0, j = read-1; i < read; i++, j--)
{
rev_line[i] = line[j];
}
printf("%s\n",rev_line);
free(rev_line);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char first[15];
printf("\n\tWrite here your code");
scanf("%s",first);
if()//there is "n" in the char
//change n with 1
else
//quit
return 0;
}
Replace character with character
This code will replace every occurrence of the character 'n' with the character '1'.
#include <stdio.h>
int main(void)
{
char str[15];
printf("Input: ");
scanf("%14s", str); // prevent buffer overflow
for (int i = 0; str[i]; ++i) { // iterate over str
if (str[i] == 'n')
str[i] = '1';
}
printf("Modified: %s\n", str);
return 0;
}
Replace character with string
This code will replace every occurrence of the character 'n' with the string "1+k" using the function strcat. The modified string is saved to char mod[].
#include <stdio.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
char str[15];
printf("Input: ");
scanf("%14s", str); // prevent buffer overflow
char mod[] = "";
for (int i = 0; str[i]; ++i) { // iterate over str
if (str[i] == 'n') {
char *repl = "1+k";
strcat(mod, repl); // add "1+k" to mod
} else {
strncat(mod, &str[i], 1); // add str[i] to mod
}
}
printf("Modified: %s\n", mod);
return 0;
}
Such a trivial question deserves a non trivial answer
Note that this approach with branching, demonstrated by #Andy Sukowski-Bang, is - when compiled with -O3 - roughly 6.5x time slower than my approach, illustratating the efficiency of bitwise operations over branching instructions (not to mention Meltdown and Spectre vulnerabilities and its on branching if mitigations are enabled).
The following program will convert every occurence of the letter from to the designated letter.
It is able to replace all characters from 'A' to 'z' to '8' in a string of 8,286,208 bytes in only 0.07s:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
// gcc -O3 replace.c && ./a.out
void replace(char *s, char from, char to, long n) {
char eq;
while (n--) {
eq = !(*s ^ from);
*s = !eq * *s + eq * to;
s++;
}
}
void replace_branching(char *s, char from, char to, long n) {
while (n--) {
if (*s == from)
*s = to;
s++;
}
}
void read_file_to_buffer(FILE *f, long length) {
char buffer[length + 1];
fseek(f, 0, SEEK_SET);
void (*fptr[2]) (char *s, char from, char to, long n) = {replace_branching, replace};
if (fread (buffer, 1, length, f) ) {
buffer[length] = '\0';
fclose (f);
char subbuff[33];
subbuff[32] = '\0';
char from = 'A';
char to = '8';
clock_t start, end;
double cpu_time_used[2];
for (int j = 0; j < 2; j++){
start = clock();
for (int i = 0; i < 0x20 + 26; i++) {
fptr[j](buffer, from + i, to, length);
//memcpy( subbuff, &buffer[0], 32 );
//printf("Modified: %s\n", subbuff);
}
end = clock();
cpu_time_used[j] = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("Time: %fs\n", cpu_time_used[j]);
}
printf("Without branching it is %fx faster\n", cpu_time_used[0] / cpu_time_used[1]);
}
}
int main(void)
{
FILE * f;
long length;
if ((f = fopen ("test.txt", "rb"))) // NB: test.txt is a file of 8,286,208 bytes
{
fseek (f, 0, SEEK_END);
read_file_to_buffer(f, ftell(f));
}
return 0;
}
/*
Time: 0.475005s
Time: 0.070859s
Without branching it is 6.703524x faster
*/
Some explanations
eq = !(*s ^ from); // eq will equal 0 if letters are same, else it will equal 1
*s = !eq * *s + eq * to; // we assign to the pointer its same old value with !eq if the character 'from' was absent, else we will assign the character 'to'.
PS: No malloc were used, I used VLA and it is bad, very bad, don't do this at home!
This is my program:
Does anyone know why it doesn't work?
My professor asked me to remove a character at an index using pointers, I'm also not allowed to use a for - loop so I'm kind of lost.
int count = 0;
int strl = strlen(s);
char s2 [strl-1];
if (index >= 0 && index < strl){
while(count < strl){
if (count == index){
*(s+index) == *s;
strl--;
}
count++;
}
printString(s);
}
}
Your program won't work because your program don't modify strings.
You can use memmove() to shift the string after the character to be removed left by one character to remove a character. (Pointers are used as the arguments of memmove())
#include <stdio.h>
#include <string.h>
void removeAt(char* str, int idx) {
size_t len = strlen(str);
memmove(str + idx, str + idx + 1, len - idx);
}
int main(void) {
char target[] = "0123456789";
printf("before removing : %s\n", target);
removeAt(target, 5);
printf("after removing : %s\n", target);
return 0;
}
Output:
before removing : 0123456789
after removing : 012346789
In order to remove a character at index i from a string you need to move every character after it one space back:
void remove_at(char* s, size_t i) {
if (!s) return;
while (s[i]) {
s[i] = s[i+1];
i++;
}
}
It's undefined behavior to pass an i >= strlen(s), so beware.
Here is an example using pointers to delete a character at a specific index in a string:
#include <assert.h>
#include <stdio.h>
#include <string.h>
void DeleteChar(int index, char string[])
{
char *ptr;
assert(index >= 0);
assert(index < strlen(string));
ptr = string + index;
while (*ptr != '\0') {
*ptr = *(ptr + 1);
ptr++;
}
}
int main(void)
{
char string[] = "hello world";
DeleteChar(9, string);
puts(string);
return 0;
}
Note, however, that it is safer and simpler to use only indices instead of pointers.
I wrote a program that reverses an array with the strrev() function and checks if its values matches the original one, sort of a palindrome. When the values match, it prints Palindrome, else, Not a palindrome.
But when I compare them, and the values don't match, it still prints Palindrome.
Here is the code:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <string.h>
#define MAX_LEN 100
void palindrom(char string[]);
int main()
{
char string[MAX_LEN] = { 0 };
printf("Enter string (max length 100 chars): ");
fgets(string, MAX_LEN, stdin);
if(string[strlen(string)-1] == '\n') { string[strlen(string)-1] = 0; }
palindrom(string);
return (0);
}
void palindrom(char string[])
{
int check = 0;
check = strcmp(strrev(string), string);
if (check == 0)
{
printf("Palindrome");
}
else
{
printf("Not a palindrome");
}
}
What's my problem? Thanks.
From what I can tell strrev may modify the original string as well, so you need to copy it.
The key is strrev.
Here's a program in C that will do what you're testing for:
#include <stdio.h>
#include <string.h>
int main()
{
char a[100], b[100];
printf("Enter the string to check if it is a palindrome\n");
fgets(a, 100, stdin);
strcpy(b,a);
strrev(b);
if (strcmp(a,b) == 0)
printf("Entered string is a palindrome.\n");
else
printf("Entered string is not a palindrome.\n");
return 0;
}
Since others have clarified what the problem is, I would like to point that it would be faster to check if s[0] == s[len-1], s[1] == s[len-2], until half (rounded up) of the string has been checked.
This would require no extra memory, no copy and half as many comparisons.
Something along the lines of:
void palindrom(char string[])
{
int len = strlen(string) - 1;
int i, limit = len/2 + (len % 2);
for (i = 0; i < limit; i++){
if (string[i] != string[len-i]){
printf("Not a palindrome\n");
return;
}
}
printf("Palindrome\n");
}
Your function fails because strrev modifies the string. You effectively always compare the reversed string to itself.
Here is an alternate function that does not modify the string:
void palindrom(const char *str) {
for (size_t i = 0, j = strlen(str); i < j; i++, j--) {
if (str[i] != str[j - 1]) {
printf("Not a palindrome\n");
return;
}
}
printf("Palindrome\n");
}
You don't need to use strrev to test for a palindrome the following function detects a palindrome just fine without using non-standard C functions:
int ispalindrome(char *str, int len)
{
char *p = &str[0];
char *q = &str[len - 1];
do
{
if(p >= q)
{
return 1;
}
} while (*p++ == *q--);
return 0;
}
I wanted to split an array to 2 arrays that the first one contains the lowercased letters of the original array and the second one contains the uppercased letters and from some reason it prints some unrelated chars.
#include <stdio.h>
#include <string.h>
#define LEN 8
int main(void)
{
char str[] = "SHaddOW";
char smallStr[LEN], bigStr[LEN];
int i = 0;
int indexSmall = 0;
int indexBig = 0;
for (i = 0; i <= LEN; i++)
{
if (str[i] <= 'Z')
{
smallStr[indexSmall] = str[i];
indexSmall++;
}
if (str[i] >= 'Z')
{
bigStr[indexBig] = str[i];
indexBig++;
}
}
printf("1: ");
puts(smallStr);
printf("2: ");
puts(bigStr);
system("PAUSE");
return 0;
}
Don't define length before you create the string to test.
Create it's length after defining the string to test.
Copy the characters as you encounter them, but as #Ed Heal says you must add a null terminator so that you can print out the two strings (they aren't really strings until they are null terminated).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main (void)
{
char str[] = "SHaddOW";
int len = strlen(str) +1;
char smallStr[len], bigStr[len];
char term[] = {'\0'};
int n, s, b;
s=0;
b=0;
for(n=0; n<len; n++) {
if(islower(str[n])) {
memcpy(smallStr +s, str +n, 1);
s++;
} else if (isupper(str[n])){
memcpy(bigStr +b, str +n, 1);
b++;
}
}
memcpy(smallStr + s, term, 1);
memcpy(bigStr + b , term, 1 );
printf("Upper: %s\n", bigStr);
printf("Lower: %s\n", smallStr);
}
Output:
Upper: SHOW
Lower: add
Add this to the if structure (and other code to support it)
} else {
memcpy(anyStr +a, str +n, 1);
a++;
}
then:
char str[] = ".S1H2a3d4d5O6W.";
and:
printf("Anything else: %s\n", anyStr);
returns:
Upper: SHOW
Lower: add
Anything else: .123456.
A more compact approach with (perhaps) more meaningful variable names:
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
#include <string.h>
int main ( void ) {
const char str[] = "SHaddOW";
size_t len = strlen(str); /* better to get actual length */
char lowers[len + 1]; /* add one for the nul char */
char uppers[len + 1]; /* (see below) */
int c;
int i = 0;
int n_upper = 0;
int n_lower = 0;
while ((c = str[i++]) != '\0') {
if (isupper(c)) uppers[n_upper++] = c; /* no need to reinvent */
if (islower(c)) lowers[n_lower++] = c; /* the wheel here */
}
uppers[n_upper] = '\0'; /* the nul char ('\0') marks */
lowers[n_lower] = '\0'; /* the end of a C "string" */
printf("1: %s\n", lowers);
printf("2: %s\n", uppers);
return 0;
}
Notes
If you are super concerned about efficiency you could add an else before if (islower...
Adding const means you "promise" the characters in the array won't be changed.
The type size_t is an integer type, but may be larger than int. It is the correct type for the return of strlen(). It is defined in <stdint.h>. None the less, using int will almost always work (on most systems a string would have to be 'yooooge' for its length to be bigger than an int can hold).
The variable c is declared as int instead of char because int is the proper type for the isXXXXX() functions (which are defined in <ctype.h>). It is also a good habit to get into because of the parallels between this loop and another common idiom while ((c = fgetc(fp)) != EOF) ....
You should consider using isupper() and islower() functions. Code would be cleaner. And what if you have some non alpha characters? Your conditions won't work.
for (i = 0; i < LEN; i++)
{
if (islower(str[i]))
{
smallStr[indexSmall] = str[i];
indexSmall++;
}
else if (isupper(str[i]))
{
bigStr[indexBig] = str[i];
indexBig++;
}
}
As #Ed Heal mention. To avoid printing rubbish, after for loopt you should add a null characters to arrays.
smallStr[indexSmall] = '\0';
bigStr[indexBig] = '\0';