Having this:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char *up(char *);
int main() {
char initstr[20];
printf("enter string\n");
fgets(initstr, 20, stdin);
char *str = up(initstr);
printf("%s\n", str);
}
char *up(char *in) {
char *ret;
for (ret = in;
*in != '\n';
*(ret++) = toupper(*(in++))
);
return ret;
}
Run it as:
$./a.out
enter string
abc
#only new line from `printf("%s\n",str);`
From debugger
Hardware watchpoint 3: in
Old value = 0x7fffffffdc20 "abc\n"
New value = 0x7fffffffdc21 "bc\n"
Hardware watchpoint 2: ret
Old value = 0x7fffffffdc20 "abc\n"
New value = 0x7fffffffdc21 "bc\n"
Hardware watchpoint 3: in
Old value = 0x7fffffffdc21 "bc\n"
New value = 0x7fffffffdc22 "c\n"
Hardware watchpoint 2: ret
Old value = 0x7fffffffdc21 "bc\n"
New value = 0x7fffffffdc22 "c\n"
...
I can see that both variables are reducing, but I wanted to change the ret inline, char by char. But at the end (after loop), the ret is reduced to nothing, and the program will only output \n. So how can I achieve this in the loop head?
EDIT:
Thanks to answer below, having in mind I have to return first address of pointer, I can implement loop_head-only function by this:
char *up(char *in){
char *ret;
size_t size=strlen(in);
for(ret=in;
*in!='\n';
*(ret++)=toupper(*(in++))
);
return (ret-size+1);
}
The bug in up is you increment ret all the way to the newline (\n) and return ret pointing to this character in the string. You should instead return a pointer to the initial character.
It is simpler to write this function using an index.
packing all the logic into the for clauses with an empty body is hard to read and error prone.
Note also that the string might not contain a newline, so it is safer to stop at the null terminator, the newline will not be changed by toupper().
Finally, you should not pass char values to toupper() because this function and all functions from <ctype.h> is only defined for values of type unsigned char and the special negative value EOF. On platforms where char is signed by default, the string might contain negative char values which may cause undefined behavior when passed to toupper(). Cast these as (unsigned char) to avoid this issue.
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
char *up(char *s) {
for (size_t i = 0; s[i] != '\0'; i++) {
s[i] = toupper((unsigned char)s[i]);
}
return s;
}
int main() {
char initstr[20];
printf("enter string\n");
if (fgets(initstr, sizeof initstr, stdin)) {
char *str = up(initstr);
printf("%s\n", str);
}
return 0;
}
Related
I am very new to C, and I have created a function that removes special characters from a string and returns a new string (without the special characters).
At first glance, this seemed to be working well, I now need to run this function on the lines of a (huge) text file (1 Million sentences). After a few thousand lines/sentences (About 4,000) I get a seg fault.
I don't have much experience with memory allocation and strings in C, I have tried to figure out what the problem with my code is, unfortunately without any luck.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char *preproccessString(char *str) {
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = malloc(sizeof(str));
// Array of allowed chars with a 0 on the end to know when the end of the array is reached, I don't know if there is a more elegant way to do this
// Changed from array to string for sake of simplicity
char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Initalize two integers
// i will be increased for every char in the string
int i = 0;
// j will be increased every time a new char is added to the result
int j = 0;
// Loop over the input string
while (str[i] != '\0') {
// l will be increased for every char in the allowed chars array
int l = 0;
// Loop over the chars in the allowed chars array
while (allowedCharsArray[l] != '\0') {
// If the char (From the input string) currently under consideration (index i) is present in the allowed chars array
if (allowedCharsArray[l] == toupper(str[i])) {
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = toupper(str[i]);
j++;
}
l++;
}
i++;
}
return result;
}
Here is the rest of the program, I think the problem is probably here.
int main(int argc, char *argv[]) {
char const * const fileName = argv[1];
FILE *file = fopen(fileName, "r");
char line[256];
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preproccessString(line));
}
fclose(file);
return 0;
}
You have several problems.
You're not allocating enough space. sizeof(str) is the size of a pointer, not the length of the string. You need to use
char *result = malloc(strlen(str) + 1);
+ 1 is for the terminating null byte.
You didn't add a terminating null byte to the result string. Add
result[j] = '\0';
before return result;
Once you find that the character matches an allowed character, there's no need to keep looping through the rest of the allowed characters. Add break after j++.
Your main() function is never freeing the results of preprocessString(), so you might be running out of memory.
while (fgets(line, sizeof(line), file)) {
char *processed = preproccessString(line);
printf("%s\n", processed);
free(processed);
}
You could address most of these problems if you have the caller pass in the result string, instead of allocating it in the function. Just use two char[256] arrays in the main() function.
int main(int argc, char *argv[])
{
char const* const fileName = argv[1];
FILE* file = fopen(fileName, "r");
char line[256], processed[256];
while (fgets(line, sizeof(line), file)) {
processString(line, processed);
printf("%s\n", processed);
}
fclose(file);
return 0;
}
Then just change the function so that the parameters are:
void preprocessString(const char *str, char *result)
A good rule of thumb is to make sure there is one free for every malloc/calloc call.
Also, a good tool to keep note of for the future is Valgrind. It's very good at catching these kinds of errors.
There are some major issues in your code:
the amount of memory allocated is incorrect, sizeof(str) is the number of bytes in a pointer, not the length of the string it points to, which would also be incorrect. You should write char *result = malloc(strlen(str) + 1);
the memory allocated in preproccessString is never freed, causing memory leaks and potentially for the program to run out of memory on very large files.
you do not set a null terminator at the end of the result string
Lesser issues:
you do not check if filename was passed nor if fopen() succeeded.
there is a typo in preproccessString, it should be preprocessString
you could avoid memory allocation by passing a properly sized destination array.
you could use isalpha instead of testing every letter
you should cast the char values as unsigned char when passing them to toupper because char may be a signed type and toupper is undefined for negative values except EOF.
there are too many comments in your source file, most of which are obvious but make the code less readable.
Here is a modified version:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
// transform the string in `str` into buffer dest, keeping only letters and uppercasing them.
char *preprocessString(char *dest, const char *str) {
int i, j;
for (i = j = 0; str[i] != '\0'; i++) {
if (isalpha((unsigned char)str[i])
dest[j++] = toupper((unsigned char)str[i]);
}
dest[j] = '\0';
return dest;
}
int main(int argc, char *argv[]) {
char line[256];
char dest[256];
char *filename;
FILE *file;
if (argc < 2) {
fprintf(stderr, "missing filename argument\n");
return 1;
}
filename = argv[1];
if ((file = fopen(filename, "r")) == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
return 1;
}
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preprocessString(dest, line));
}
fclose(file);
return 0;
}
The following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
properly checks for length of input string parameter
makes use of characteristic of strchr() also checking the terminating NUL byte
limits scope of visibility of local variables
the calling function is expected to properly cleaning up by passing the returned value to free()
the calling function is expected to check the returned value for NULL
informs compiler the user knows and accepts when an implicit conversion is made.
moves allowedCharsArray to 'file static scope' so does not have to be re-initialized on each pass through the loop and marks as 'const' to help the compiler catch errors
and now the proposed code: (note: edited per comments)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
char *preproccessString(char *str)
{
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = calloc( sizeof( char ), strlen(str)+1);
if( !result )
{
perror( "calloc failed" );
return NULL;
}
// Array of allowed chars
static const char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Loop over the input string
for( int j=0, i=0; str[i]; i++)
{
if( strchr( allowedCharsArray, (char)toupper( str[i] ) ) )
{
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = (char)toupper(str[i]);
j++;
}
}
return result;
}
I think the problem is you are using malloc which allocates memory from the heap and since you are calling this function again and again you are running out of memory.
To solve this issue you have to call the free() function on the pointer returned by your preprocessString function
In your main block
char *result=preprocessString(inputstring);
//Do whatever you want to do with this result
free(result);
In an effort of solving a textbook problem, I'm trying to create a case insensitive version of the function called strstr() which is in the C language. So far, I've run into two problems. The first problem being that when I make the case insensitive version of strstr() it worked, but it didn't stop at the first matching string and continued to return the string even if they didn't match.
strstr() is supposed to see the first instance of a matching character up to n counts specified and then stop. Like if I wrote: "Xehanort" in string A and "Xemnas" in string B and specified 4, as the number, it would return Xe.
The idea behind the case insensitive version is that I can write : "Xehanort" in one string and "xemnas" in the next string and have it return Xe.
However, I've run into a new problem in new code I've tried: the function doesn't seem to want to run at all. I've tested this and it turns out the function seems to be at a crash and I'm not sure how to make it stop.
I've tried editing the code, I've tried using different for loops but figured that the code doesn't need to be too sophisticated yet, I've also tried different code entirely than what you are going to read, but that resulted in the problem mentioned earlier.
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAX 100
char *stristr4(const char *p1, const char *p2, size_t num);
int main() {
char c[MAX], d[MAX];
printf("Please enter the string you want to compare.");
gets(c);
printf("Please enter the next string you want to compare.");
gets(d);
printf("The first string to be obtained from \n%s, and \n%s is \n%s",
c, d, stristr4(c, d, MAX));
}
char *stristr4(const char *p1, const char *p2, size_t num) {
const char *str1 = p1;
const char *str2 = p2;
char *str3;
int counter = 0;
for (int i = 0; i < num; i++) {
for (int j = 0; j < num; j++) {
if (tolower(str1[i]) == tolower(str2[j])) {
str3[i] = str1[i];
counter++;
} else {
if (counter > 0) {
break;
} else
continue;
}
}
}
return str3;
}
The code you see will ask for the strings you want to input. Ideally, it should return the input.
Then it should do the stristr function and return the first instance of matching string with case insensitivity.
However, the function I've created doesn't even seem to run.
Your code has undefined behavior (in this case causing a segmentation fault), because you try to store the resulting string via an uninitialized pointer str3.
Standard function strstr returns a pointer to the matching subsequence, you should do the same. The third argument is useless if the first and second arguments are proper C strings.
Here is a modified version:
char *stristr4(const char *p1, const char *p2) {
for (;; p1++) {
for (size_t i = 0;; i++) {
if (p2[i] == '\0')
return (char *)p1;
if (tolower((unsigned char)p1[i]) != tolower((unsigned char)p2[i]))
break;
}
if (*p1 == '\0')
return NULL;
}
}
Notes:
function tolower() as other functions from <ctype.h> takes an int argument that must have the value of an unsigned char or the special negative value EOF. char arguments must be converted to unsigned char to avoid undefined behavior for negative char values. char can be signed or unsigned by default depending on the platform and the compilers settings.
you should never use gets(). This function is obsolete and cannot be used safely with uncontrolled input. Use fgets() and strip the trailing newline:
if (fgets(c, sizeof c, stdin)) {
c[strcspn(c, "\n")] = '\0'; // strip the trailing newline if any
...
}
A third string could be passed to the function and fill that string with the matching characters.
Use fgets instead of gets.
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#define MAX 100
int stristr4(const char* p1, const char *p2, char *same);
int main( void)
{
int comp = 0;
char c[MAX] = "", d[MAX] = "", match[MAX] = "";//initialize to all zero
printf ( "Please enter the string you want to compare. ");
fflush ( stdout);//printf has no newline so make sure it prints
fgets ( c, MAX, stdin);
c[strcspn ( c, "\n")] = 0;//remove newline
printf ( "Please enter the next string you want to compare. ");
fflush ( stdout);//printf has no newline so make sure it prints
fgets ( d, MAX, stdin);
d[strcspn ( d, "\n")] = 0;//remove newline
comp = stristr4 ( c, d, match);
printf ( "Comparison of \n%s, and \n%s is \n%d\n", c, d, comp);
if ( *match) {
printf ( "The matching string to be obtained from \n%s, and \n%s is \n%s\n"
, c, d, match);
}
return 0;
}
int stristr4 ( const char *p1,const char *p2, char *same)
{
//pointers not pointing to zero and tolower values are equal
while ( *p1 && *p2 && tolower ( (unsigned char)*p1) == tolower ( (unsigned char)*p2))
{
*same = tolower ( (unsigned char)*p1);//count same characters
same++;//increment to next character
*same = 0;//zero terminate
p1++;
p2++;
}
return *p1 - *p2;//return difference
}
I have this program that's supposed to be a 'chat simulator', the only thing it's supposed to do now is replying 'Hello!' when the user types 'Hello'.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
int main()
{
printf("Chat simulator!\n");
do {
char x;
printf("[User1] ");
scanf("%s",&x);
if (strcmp (x,"Hello") == 0)
{
Sleep(1500);
printf("[User2] Hello!\n");
}
else {}
} while(1);
}
I know that strcmp is only for const char *, not a single char, and that's the problem here, but I couldn't find any other solution for this, since I need to use char x in scanf, so it can't be a const char *. Also it may be possible that I'm using strcmp wrong.
Code:Blocks warning:
passing argument 1 of 'strcmp' makes pointer from integer without a cast*
expected 'const char *' but argument is of type 'char'*
Edit:
So I changed the char to char[16] as #robin.koch told me, and it's all working as it should. Thanks!
You cannot compare a string with a char with strcmp, but it is easy to do by hand:
int samechar(const char *str, char c) {
return *str == c && (c == '\0' || str[1] == '\0');
}
The above function however is not what you need for you problem:
You should read a string from the user, not a single char.
scanf() needs a pointer to a char array for the conversion specifier %s.
Furthermore you should specify the maximum number of characters to store into the this array to avoid potential a buffer overflow.
Finally, scanf() will only read a single word. You probably want to read a full line from the user. Use fgets() for this.
Here is a modified version:
#include <stdio.h>
#include <string.h>
int main(void) {
printf("Chat simulator!\n");
for (;;) {
char buf[100];
printf("[User1] ");
if (!fgets(buf, sizeof buf, stdin))
break;
buf[strcspn(buf, "\n")] = '\0'; /* strip the newline if present */
if (strcmp(buf, "Hello") == 0) {
printf("[User2] Hello!\n");
}
}
return 0;
}
As others have pointed out, you're trying to store a string into a char variable using scanf when chars are only meant to store one character. You should you use a char * or char[] variable to hold your string instead. So change
char x;
printf("[User1] ");
scanf("%s",&x);
//...rest of your code...
to
char * x = malloc(sizeof(char) * 10); //can hold up to ten characters
printf("[User1] ");
scanf("%s",x);
//...rest of your code...
free(x);
Note that if you just want to use a char array instead of a pointer you can replace the first line above with something like char x[10]; and get rid of free(x);
I'm new to C language and I need a help on String functions.
I have a string variable called mcname upon which I would like to compare the characters between special characters.
For example:
*mcname="G2-99-77"
I expect the output to be 99 as this is between the - characters.
How can I do this in C please?
Travel the string (walking pointer) till u hit a special character.
Then start copying the characters into seperate array untill u hit the next special character (Place a null character when u encounter the special character second time)
You can do this by using strtok or sscanf
using sscanf:
#include <stdio.h>
int main()
{
char str[64];
int out;
char mcname[] = "G2-99-77";
sscanf(mcname, "%[^-]-%d", str, &out);
printf("%d\n", out);
return 0;
}
Using strtok:
#include <stdio.h>
#include <string.h>
int main()
{
char *str;
int out;
char mcname[] = "G2-99-77";
str = strtok(mcname, "-");
str = strtok (NULL, "-");
out = atoi(str);
printf("%d\n", out);
return 0;
}
sscanf() has great flexibility. Used correctly, code may readily parse a string.
Be sure to test the sscanf() return value.
%2[A-Z0-9] means to scan up to 2 characters from the set 'A' to 'Z' and '0' to '9'.
Use %2[^-] if code goal is any 2 char other than '-'.
char *mcname = "G2-99-77";
char prefix[3];
char middle[3];
char suffix[3];
int cnt = sscanf(mcname, "%2[A-Z0-9]-%2[A-Z0-9]-%2[A-Z0-9]", prefix, middle,
suffix);
if (cnt != 3) {
puts("Parse Error\n");
}
else {
printf("Prefix:<%s> Middle:<%s> Suffix:<%s>\n", prefix, middle, suffix);
}
I don't understand why the program below returns "z#" as string. But when I put a printf("%s \n",""); before that, then it returns "11110111" (as aspected).
Can anybody help me to understand this behaviour?
Many thanks in advance!
Please find my whole program posted below:
#include <stdio.h>
char * bin2str(unsigned char bin) {
char str[] = "00000000";
int i;
for (i=7; i>=0; i--) {
if ((bin%2)==0) {
str[i] = '0';
} else {
str[i] = '1';
}
bin = bin >> 1;
}
return ((char *) str);
}
int main() {
unsigned char testChar;
testChar |= 0b11110111;
printf("Let us test binary to string: \n\n \n");
//printf("%s \n","");
printf("%s \n", bin2str(testChar)); //now it returns rubbish, but when the previous line is uncommented it turns out to be working correctly. Why?
return 0;
}
You are returning the local variable str from the function bin2str which is undefined behaviour. Instead you could copy it using strdup() (or its equivalent using malloc() + strcpy()).
char *str = strdup("00000000");
and return it.
Don't forget to free() it!
You are generating a string as a local variable inside bin2str(). That string will be located in stack space while bin2str() is executing. As soon as the function returns, the stack space is no longer valid, and the memory pointed by str will contains whether the program puts in the stack between the return from the call to bin2str() and the call to printf() where you intend to print the contents of str.
Alternatives:
Declare str as static. This way, str won't be placed in stack space and its contents will be available even after the function ends. Beware! as this solution is not thread safe.
Change your prototype so it accepts a second argument, which will be the address of a string that the caller will have to provide, to which bin2str() will write the binary representation of the number.
Thanks you all for your feedback. Wow, that was fast!
It was my first post.
I made the changes. Please find the correct functioning program below (without compile warnings):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * bin2str(unsigned char bin) {
char *str = strdup("00000000");
int i;
for (i=7; i>=0; i--) {
if ((bin%2)==0) {
str[i] = '0';
} else {
str[i] = '1';
}
bin = bin >> 1;
}
return ((char *) str);
free(str);
}
int main() {
unsigned char testChar;
testChar |= 0b11110111;
printf("Let us test binary to string: \n\n \n");
printf("%s \n", bin2str(testChar));
return 0;
}