I am working with a pointer to an array of characters. This code is supposed to switch the cases of letters, delete digits, print two spaces instead of one, and print all other chars the same. It does all the rest fine except print other characters the same. This seems like it should be a non issue but I cannot figure it out. See anything that looks like its wrong?
void compareDuplicates(FILE * ifp, char mode){
/* size MAXCHARS+1 to hold full array + null terminate*/
char newArray [MAXCHARS +1] = {0};
char oldArray [MAXCHARS +1] = {0};
char * newStr = newArray;
char * oldStr = oldArray;
char * tempStr;
/* fill array, test for EOF*/
while(fgets(newStr,MAXCHARS, ifp)){
//if strings are the same, do not print anything
if(strcmp(newStr, oldStr) !=0){
//else print
testStrings(newStr);
}
//set oldStr pointer to newStr, set newStr pointer to oldStr reset newStr memory
//reset memory of newStr to all null chars
tempStr = oldStr;
oldStr = newStr;
newStr = tempStr;
memset(newStr,'\0',MAXCHARS);
}
}
void testStrings(char * array1){
int i = 0;
char c;
while(*(array1+i) != '\0'){
if(*(array1+i) >= 'A' && *(array1+i) <= 'Z'){
c = *(array1+i)+32;
printf("%c",c);
}
else if(*(array1+i) >= 'a' && *(array1+i) <='z'){
c = *(array1+i)-32;
printf("%c",c);
}
else if(*(array1+i) == ' '){
c = *(array1+i);
printf("%c",c);
printf("%c",c);
}
else if(*(array1+i) >= '0' || *(array1+i) <= '9'){
i++;
continue;
}
else{
c = *(array1+i);
printf("%c",c);
}
i++;
}
printf("\n");
}
for example, if given the lines:
CONSECUTIVE_LINE
CONSECUTIVE_LINE
CONSECUTIVE_LINE
123 REPEAT
123 REPEAT
232unique-line
the output will be:
consecutiveline
repeat
UNIQUELINE
representing a deletion of consecutive lines, the changing of cases, adding two spaces in the place of one and the deletion of digits. However, it will not print the normal underscores and other characters not targeted.
This test...
*(array1+i) >= '0' || *(array1+i) <= '9'
... will always yield true. Any character you check against is going to be more than '0' or less than '9' because '0' < '9'. You probably wanted to check a character is inside this range, which requires a && (logical AND), like you do in all the others.
As a side note, don't assume the character encoding is going to put the alphabetic characters in sequence. It's only guaranteed to be true for the digit characters. A better check would utilize isalpha and islower or isupper from the standard library's ctype.h header.
Related
I am fairly new to programming and I am trying to convert a string containing a base 10 number to an integer value following this pseudo algorithm in c.
start with n = 0
read a character from the string and call it c
if the value of c is between '0' and '9' (48 and 57):
n = n * 10 +(c-'0')
read the next character from the string and repeat
else return n
here is the rough basics of what i wrote down however I am not clear on how to read a character from the string. i guess im asking if i understand the pseudocode correctly.
stoi(char *string){
int n = 0;
int i;
char c;
for (i = 0;i < n ; i++){
if (c[i] <= '9' && c[i] >= '0'){
n = n *10 +(c - '0')}
else{
return n
}
}
}
You were close, you just need to traverse the string to get the value of each digit.
Basically you have two ways to do it.
Using array notation:
int stoi(const char *str)
{
int n = 0;
for (int i = 0; str[i] != '\0'; i++)
{
char c = str[i];
if ((c >= '0') && (c <= '9'))
{
n = n * 10 + (c - '0');
}
else
{
break;
}
}
return n;
}
or using pointer arithmetic:
int stoi(const char *str)
{
int n = 0;
while (*str != '\0')
{
char c = *str;
if ((c >= '0') && (c <= '9'))
{
n = n * 10 + (c - '0');
}
else
{
break;
}
str++;
}
return n;
}
Note that in both cases we iterate until the null character '\0' (which is the one that marks the end of the string) is found.
Also, prefer const char *string over char *string when the function doesn't need to modify the string (like in this case).
Congrats on starting your C journey!
One of the most important aspects of strings in C is that, technically, there are none. A string is not a primitive type like in Java. You CAN'T do:
String myString = "Hello";
In C, each string is just an array of multiple characters. That means the word Hello is just the array of [H,e,l,l,o,\0]. Here, the \0 indicates the end of the word. This means you can easily access any character in a string by using indexes (like in a normal array):
char *myString = "Hello";
printf("%c", myString[0]); //Here %c indicates to print a character
This will then print H, since H is the first character in the string. I hope you can see how you can access the any character in the string.
I need to build a function that gets an input and capitalizes only the first letter, doesn't print numbers, capitalizes after a . for a new sentence, and capitalizes all words between a double quotation marks ".
This is what I got until now:
#include <stdio.h>
#define MAX 100
int main()
{
char str[MAX] = { 0 };
int i;
//input string
printf("Enter a string: ");
scanf("%[^\n]s", str); //read string with spaces
//capitalize first character of words
for (i = 0; str[i] != '\0'; i++)
{
//check first character is lowercase alphabet
if (i == 0)
{
if ((str[i] >= 'a' && str[i] <= 'z'))
str[i] = str[i] - 32; //subtract 32 to make it capital
continue; //continue to the loop
}
if (str[i] == '.')//check dot
{
//if dot is found, check next character
++i;
//check next character is lowercase alphabet
if (str[i] >= 'a' && str[i] <= 'z')
{
str[i] = str[i] - 32; //subtract 32 to make it capital
continue; //continue to the loop
}
}
else
{
//all other uppercase characters should be in lowercase
if (str[i] >= 'A' && str[i] <= 'Z')
str[i] = str[i] + 32; //subtract 32 to make it small/lowercase
}
}
printf("Capitalize string is: %s\n", str);
return 0;
}
I cant find a way to remove all numbers from input and convert all lowercase to uppercase inside a " plus code for not printing numbers if user input them.
if I input
I am young. You are young. All of us are young.
"I think we need some help. Please" HELP. NO, NO NO,
I DO NOT
NEED HELP
WHATSOEVER.
"Today’s date is
15/2/2021"...
I am 18 years old, are you 20 years old? Maybe 30 years?
output:
I am young. You are young. All of us are young.
"I THINK WE NEED SOME HELP. PLEASE" help. No, no no,
i do not
need help
whatsoever.
"TODAY’S DATE IS
//"...
I am years old, are you years old? maybe years?
The C standard library provides a set of functions, in ctype.h, that will help you
Of particular interest, would be:
isdigit() - returns true if digit
isalpha() - returns true if alphabet character
isalnum() - returns true if alpha/numeric character
islower() - returns true if lower case character
isupper() - returns true if upper case character
tolower() - converts character to lower case
toupper() - converts character to upper case
So, for example, you could replace the test/modify with:
if ( islower( str[i] ) )
{
str[i] = toupper( str[i] );
}
Pedantically, islower() and toupper() return an unsigned int but that's a separate matter...
You can remove letters from a string if you keep two indices, one for reading and one for writing. The following loop will remove all digits from a string:
int j = 0; // writing index, j <= i
int i; // reading index
for (i = 0; str[i]; i++) {
int c = (unsigned char) str[i];
if (!isdigit(c)) str[j++] = c;
}
str[j] = '\0';
(I've used to character classification functions from <ctype.h> mentioned in Andrew' answer.)
This is safe, because j will always be smaller or equal to i. Don't forget to mark the end of the filtered string with the nullterminator, '\0'. You can combine this filtering with your already existing code for replacing characters.
In your code, you capitalize letters only if they are directly behind a full stop. That's usually not the case, there's a space between full stop and the next word. It's better to establish a context:
shift: capitalize the next letter (beginning or after full stop.)
lock: capitalize all letters (inside quotation marks.)
When you read a letter, decide whether to capitalize it or not depending of these two states.
Putting the filtering and the "shift context§ together:
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char str[] = "one. two. THREE. 4, 5, 6. \"seven\", eight!";
int shift = 1; // Capitalize next letter
int lock = 0; // Capitalize all letters
int j = 0; // writing index, j <= i
int i; // reading index
for (i = 0; str[i]; i++) {
int c = (unsigned char) str[i];
if (isdigit(c)) continue;
if (isalpha(c)) {
if (shift || lock) {
str[j++] = toupper(c);
shift = 0;
} else {
str[j++] = tolower(c);
}
} else {
if (c == '"') lock = !lock;
if (c == '.') shift = 1;
str[j++] = c;
}
}
str[j] = '\0';
puts(str);
printf("(length: %d)\n", j);
return 0;
}
In order to remove some characters, you should use 2 index variables: one for reading and one for writing back to the same array.
If you are allowed to use <ctype.h>, it is a much more portable and efficient way to test character types.
Also do not use scanf() with protection against buffer overflow. It is as bad as using gets(). Given the difficulty in specifying the maximum number of bytes to store into str, you should use fgets() instead of scanf().
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#define MAX 100
int main() {
char str[MAX];
int i, j;
unsigned char last, inquote;
//input string
printf("Enter a string: ");
if (!fgets(str, sizeof str, stdin)) { //read string with spaces
// empty file
return 1;
}
last = '.'; // force conversion of first character
inquote = 0;
//capitalize first character of words
for (i = j = 0; str[i] != '\0'; i++) {
unsigned char c = str[i];
//discard digits
if (isdigit(c)) {
continue;
}
//handle double quotes:
if (c == '"') {
inquote ^= 1;
}
//upper case letters after . and inside double quotes
if (last == '.' || inquote) {
str[j++] = toupper(c);
} else {
str[j++] = tolower(c);
}
if (!isspace(c) && c != '"') {
// ignore spaces and quotes for the dot rule
last = c;
}
}
str[j] = '\0'; // set the null terminator in case characters were removed
printf("Capitalized string is: %s", str);
return 0;
}
I've got a function which does some stuff with strings, however it has to save the original string by copying it into a char array, making it all upper-case and substitute any w/W for V.
char* function(const char* text){
int textLength = strlen(text);
char text_copy[textLength];
for(int i = 0; i < textLength; i++){
if(text[i] == 'W' || text[i] == 'w')
text_copy[i] = 'V';
else
text_copy[i] = toupper(text[i]);
}
return 'a';
}
It doesn't really matter what the function returns, however whenever I try to printf("%s\n", text_copy);, with some strings, it returns this:
belfast: BELFAST
please: PLEASE
aardvark: AARDVARK??
hello world: HELLO VORLD
taxxxiii: TAXXXIII???
swag: SVAG?
Why is it that some strings turn out fine and some don't? Thanks.
You need to null-terminate the copy.
char text_copy[textLength+1];
...
text_copy[textLength]='\0';
Though if you are returning it from your function (that isn't clear) you should be mallocing it instead.
Why is it that some strings turn out fine and some don't?
Pure chance.
You only allocate enoufgh space for the visible characters in the string and not the terminating \0. You are just lucky that for some of the strings a null byte is on the stack just after the character array.
Change your code like so...
int textLength = strlen(text);
char text_copy[textLength + 1]; // << enough space for the strings and \0
for(int i = 0; i < textLength; i++){
if(text[i] == 'W' || text[i] == 'w')
text_copy[i] = 'V';
else
text_copy[i] = toupper(text[i]);
}
text_copy[textLength] = '\0'; // Make sure it is terminated properly.
I want to use pipes to read a string in child process and send it to the parent, then count the number of characters in the string and display the number. The program runs only until I input a string. Can you please tell me what I'm doing wrong?
#include <stdlib.h>
#include <stdio.h>
#include<string.h>
#define SIZE 1024
int main()
{
int i,n,x;
char msg[SIZE];
char str[SIZE];
int p3[2];
pipe(p3);
int pid=fork();
if(pid==0){
close(p3[0]); //closing read end
printf("Enter a string\n");
fgets(msg,SIZE,stdin);
write(p3[1],msg,sizeof(msg)); //writting into pipe
close(p3[1]); //closing write end
}
else {
close(p3[1]); //close write end
read(p3[0], str, sizeof(str));
int c = 0;
while (str[c] != "Done")
{
for(i=0; i<sizeof(str);i++){
if (str[c] >= 'a' && str[c] <= 'z') {
c++;
}
}
}
printf("%d letters\n", c);
}
return 0;
}
Use strlen to determine the length of the string then use for loop to iterate upto the length, something like below:
if (strncmp(str, "Done", 4) == 0)
exit(0);
int len = strlen(str);
for (l = 0; l < len; l++)
{
if (str[l] >= 'a' && str[l] <= 'z')
{
c++;
}
}
printf("%d letters\n", c);
close(p3[0]); //close write end
You can use strlen() to determine the length of any null-terminated string;
In while (str[c] != "Done") you're comparing a char to a string literal. You should use strcmp() or strncmp() to compare strings;
The inner-loop (for(i=0; i<sizeof(str);i++) {...}) is using str[c], but I believe you want str[i], besides, the loop is not counting (c++) when the char is not in the interval [a,z], therefore your outer-loop may never end;
You really don't need nested loops. Try something like:
const char *ptr = str;
size_t index = 0; // An index to access str - just to help you.
// Iterate over ptr, one character at a time, until we find the null terminator.
while (*ptr != '\0') {
// Read 4 chars from ptr and check if it's "Done"
if (strncmp(ptr, "Done", 4) == 0)
break;
// Do something here. For example:
// if (str[index] >= 'a' && str[index] <= 'z')
// lowercase_counter++;
// if (str[index] >= 'A' && str[index] <= 'Z')
// uppercase_counter++;
// if (str[index] >= '0' && str[index] <= '9')
// number_counter++;
// NOTE: str[index] is equivalent to *ptr, thus you can use one or another.
ptr++;
index++;
}
Be sure to not increment ptr elsewhere - the idea is for each loop iteration to check a single character - you can still read ahead using strcmp or whatever, like we did for "Done". If you do that, also take care to not access ptr beyond the str boundaries.
We have a function longest, which returns the longest substring that consists of letters. Example:
longest("112****hel 5454lllllo454")
would return: lllllo
However, when I run the program it seems to return lllllo454. Here is the function:
char *longest(char *s){
char *pMax = NULL;
int nMax = 0;
char *p = NULL;
int n = 0;
int inside = 0; //flag
while(*s!='\0'){
char c = *s;
if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')){
if(inside == 0){
n = 1;
p = s;
inside = 1;
}
else
n++;
if(inside == 1){
if(n > nMax){
nMax = n;
pMax = p;
inside = 0;
}
}
}//end isLetter if
s++;
}
return pMax;
}
There's something I'm not seeing here...what do you guys think?
You are just returning a pointer to the first character in the longest substring. You don't actually add a string terminator after the end of the substring, so it continues to the end of the original string. You probably ought to copy the substring (only those characters in the sequence) to a new string and return a pointer to that.
char* newStr = malloc(nMax+1);
strncpy( newStr, pMax, nMax );
*(newStr+nMax) = '\0';
return newStr;
You are calculating nMax but not doing anything with that information. In C, a char* points to the start of a string of characters, which is terminated by a NUL character. Since you are not modifying the buffer passed to your function, the returned pointer points to the first 'l' and continues to the end of the original string.
You are returning the pointer to the first letter of the longest substring; you are not making a new string out of it. Thus when you print it out, it prints the substring up until the NULL terminator.
See the strncpy function: http://www.cplusplus.com/reference/clibrary/cstring/strncpy/
This returns the largest substring that starts with a string. The part of the function that sets inside, as follows:
if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')){
will only be executed if c is a letter. Since you want the largest substring that includes a letter, you need this to be:
if(c != ' '){
Then, inside that loop, have another variable, say containsLetter, that is only true if you encounter a letter before another space.