I'm stuck on trying to figure out how to make certain letters of a character string in an array that are input as all lower case and an underscore (i.e. "first_last") output with a space between them and each beginning letter being capitalized (i.e. "First Last").
Any helpful ideas are appreciated
Here's what I have so far:
#include <stdio.h>
#define SIZE 30
int main()
{
char string1(SIZE); //reserve a name of up to 29 characters
//read characters from input into array string 1
printf("%s", "Enter a name:);
scanf("%29s", string1);
}
You can just process a string character by character and modify the relevant characters, something like:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// Capitalise based on your specs.
static void cap(char *str) {
// No actions for empty string.
if (*str == '\0') return;
// Always upper first character.
str[0] = toupper(str[0]);
// Check all but last character (ignore trailing '_').
for (size_t i = 1, count = strlen(str) - 1; i < count; ++i) {
// If underscore, make space and upper next character.
if (str[i] == '_') {
str[i] = ' ';
str[i+1] = toupper(str[i+1]);
}
}
}
// Test driver to do all arguments.
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; ++i) {
printf("'%s'", argv[i]);
cap(argv[i]);
printf("-> '%s'\n", argv[i]);
}
}
You'll probably want to handle edge cases but, since you haven't specified the desired behaviour for them, I've just left them as further exercises for the reader. Things to think about are:
how to handle string with multiple____underscores;
how to treat _ at end of string;
how to treat _ if not followed by a lower-case letter; and
quite possibly other things I haven't thought of :-)
Here's a sample run so you can see it in action:
pax$ testprog first_second one one_two
'first_second'-> 'First Second'
'one'-> 'One'
'one_two'-> 'One Two'
Related
I'm trying to write a C program that only prints the last occurrence of repeating letters of a string. I have that part done but I want to store all those chars in a string. What I have so far is:
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
bool isLast(char *arg1, char ch, int p) {
p++;
while (arg1[p] != '\0') {
if ((arg1[p]) == ch) {
return false;
}
p++;
}
return true;
}
int main() {
char *word = "kaax";
char *vals = "1235";
char *result = "";
for (int i = 0; word[i] != '\0'; i++) {
if (isLast(word, word[i], i)) {
result += vals[i];
}
}
printf("%s", result);
}
I want:
printf("%s",result);
to print:
fxkav
Since that is the logical result of my program and what the output should be.
How to phrase it ... Not at all.
Your "empty string" is a string literal of size 1 (containing only '\0'. It cannot be changed and even lesss extended.
If you need to manipulate "strings" (which in C is not really an existing concept) you need to represent them as sequences of characters, which are stored in a data structure which allows to change the contained characters and, in your case, also has space for more characters.
In cases where you can determine a maximum size (MAXSIZE) you could define an array of characters of that size like this
char SizedCharArray[MAXSIZE];
Im trying to write a C program that removes all occurrences of repeating chars in a string except the last occurrence.For example if I had the string
char word[]="Hihxiivaeiavigru";
output should be:
printf("%s",word);
hxeavigru
What I have so far:
#include <stdio.h>
#include <string.h>
int main()
{
char word[]="Hihxiiveiaigru";
for (int i=0;i<strlen(word);i++){
if (word[i+1]==word[i]);
memmove(&word[i], &word[i + 1], strlen(word) - i);
}
printf("%s",word);
return 0;
}
I am not sure what I am doing wrong.
With short strings, any algorithm will do. OP's attempt is O(n*n) (as well as other working answers and #David C. Rankin that identified OP's short-comings.)
But what if the string was thousands, millions in length?
Consider the following algorithm: #paulsm4
Form a `bool` array used[CHAR_MAX - CHAR_MIN + 1] and set each false.
i,unique = n - 1;
From the end of the string (n-1 to 0) to the front:
if (character never seen yet) { // used[] look-up
array[unique] = array[i];
unique--;
}
Mark used[array[i]] as true (index from CHAR_MIN)
i--;
Shift the string "to the left" (unique - i) places
Solution is O(n)
Coding goal is too fun to just post a fully coded answer.
I would first write a function to determine if a char ch at a given position i is the last occurence of ch given a char *. Like,
bool isLast(char *word, char ch, int p) {
p++;
ch = tolower(ch);
while (word[p] != '\0') {
if (tolower(word[p]) == ch) {
return false;
}
p++;
}
return true;
}
Then you can use that to iteratively emit your desired characters like
int main() {
char *word = "Hihxiivaeiavigru";
for (int i = 0; word[i] != '\0'; i++) {
if (isLast(word, word[i], i)) {
putchar(word[i]);
}
}
putchar('\n');
}
And (for completeness) I used
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
Outputs (as requested)
hxeavigru
Additional areas where you are currently hurting yourself.
Your for loop must NOT increment the index, e.g. for (int i=0; word[i];). This is because when you memmove() by 1, you have just incremented the indexes. That also means the value to save for last is now i - 1.
there should only be one call to strlen() in the program. You can simply subtract one from length each time memmove() is called.
only increment your loop counter variable when memmove() is not called.
Additionally, avoid hardcoding strings. You shouldn't have to recompile your code just to test the results of "Hihxiivaeiaigrui" instead of "Hihxiivaeiaigru". You shouldn't have to recompile just to remove all but the last 'a' instead of the 'i'. Either pass the string and character to find as arguments to your program (that's what int argc, char **argv are for), or prompt the user for input.
Putting it altogether you could do (presuming word is 1023 characters or less):
#include <stdio.h>
#include <string.h>
#define MAXC 1024
int main (int argc, char **argv) {
char word[MAXC]; /* storage for word */
strcpy (word, argc > 1 ? argv[1] : "Hihxiivaeiaigru"); /* copy to word */
int find = argc > 2 ? *argv[2] : 'i', /* character to find */
last = -1; /* last index where find found */
size_t len = strlen (word); /* only compute strlen once */
printf ("%s (removing all but last %c)\n", word, find);
for (int i=0; word[i];) { /* loop over each char -- do NOT increment */
if (word[i] == find) { /* is this my character to find? */
if (last != -1) { /* if last is set */
/* overwrite last with rest of word */
memmove (&word[last], &word[last + 1], (int)len - last);
last = i - 1; /* last now i - 1 (we just moved it) */
len = len - 1;
}
else { /* last not set */
last = i; /* set it */
i++; /* increment loop counter */
}
}
else /* all other chars */
i++; /* just increment loop counter */
}
puts (word); /* output result -- no need for printf (no coversions) */
}
Example Use/Output
$ ./bin/rm_all_but_last_occurrence
Hihxiivaeiaigru (removing all but last i)
Hhxvaeaigru
What if you want to use "Hihxiivaeiaigrui"? Just pass it as the 1st argument:
$ ./bin/rm_all_but_last_occurrence Hihxiivaeiaigrui
Hihxiivaeiaigrui (removing all but last i)
Hhxvaeagrui
What if you want to use "Hihxiivaeiaigrui" and remove duplicate 'a' characters? Just pass the string to search as the 1st argument and the character to find as the second:
$ ./bin/rm_all_but_last_occurrence Hihxiivaeiaigrui a
Hihxiivaeiaigrui (removing all but last a)
Hihxiiveiaigrui
Nothing removed if only one of the characters:
$ ./bin/rm_all_but_last_occurrence Hihxiivaeiaigrui H
Hihxiivaeiaigrui (removing all but last H)
Hihxiivaeiaigrui
Let me know if you have further questions.
Im trying to write a C program that removes all occurrences of repeating chars in a string except the last occurrence.
Process the string (or word) from last character and move towards the first character of string (or word). Now, think of it as a problem where you have to remove all occurrence of a character from string and except the first occurrence. Since, we are processing the string from last character to first character, so, we have to move the characters, which are remain after removing duplicates, to the start of string once you have processed whole string and, if, there were duplicate characters found in the string. The complexity of this algorithm is O(n).
Implementation:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define INDX(x) (tolower(x) - 'a')
void remove_dups_except_last (char str[]) {
int map[26] = {0}; /* to keep track of a character processed */
size_t len = strlen (str);
char *p = str + len; /* pointer pointing to null character of input string */
size_t i = 0;
for (i = len; i != 0; --i) {
if (map[INDX(str[i - 1])] == 0) {
map[INDX(str[i - 1])] = 1;
*--p = str[i - 1];
}
}
/* if there were duplicates characters then only copy
*/
if (p != str) {
for (i = 0; *p; ++i) {
str[i] = *p++;
}
str[i] = '\0';
}
}
int main(int argc, char* argv[])
{
if (argc != 2) {
printf ("Invalid number of arguments\n");
return -1;
}
char str[1024] = {0};
/* Assumption: the input string/word will contain characters A-Z and a-z
* only and size of input will not be more than 1023.
*
* Leaving it up to you to check the valid characters in input string/word
*/
strcpy (str, argv[1]);
printf ("Original string : %s\n", str);
remove_dups_except_last (str);
printf ("Removed duplicated characters except the last one, modified string : %s\n", str);
return 0;
}
Testcases output:
# ./a.out Hihxiivaeiavigru
Original string : Hihxiivaeiavigru
Removed duplicated characters except the last one, modified string : hxeavigru
# ./a.out aa
Original string : aa
Removed duplicated characters except the last one, modified string : a
# ./a.out a
Original string : a
Removed duplicated characters except the last one, modified string : a
# ./a.out TtYyuU
Original string : TtYyuU
Removed duplicated characters except the last one, modified string : tyU
You can re-iterate to get each characters of your string, then if it is not "i" and not the last occurrence of the i, copy to a new string.
#include <stdio.h>
#include <string.h>
int main() {
char word[]="Hihxiiveiaigru";
char newword[10000];
char* ptr = strrchr(word, 'i');
int index=0;
int index2=0;
while (index < strlen(word)) {
if (word[index]!='i' || index ==(ptr - word)) {
newword[index2]=word[index];
index2++;
}
index++;
}
printf("%s",newword);
return 0;
}
Problem: http://docs.cs50.net/problems/initials/more/initials.html
As I said in the title, I can't seem to get the program to output the initials with no spaces if the user inputs extra spaces before the name or inputs extra spaces between the first and last name.
Right now, it works only if I input my name like:First Last with no spaces before the name and only one space inbetween the two words. It will print out FL without any additional spaces. I want it to do this no matter how many extra spaces I have before or inbetween the first and last name.
My current code:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(void) {
printf("Name: ");
string s = get_string();
printf("%c", toupper(s[0]));
for (int i = 0; i < strlen(s); i++) {
if (s[i] == ' ') {
printf("%c", toupper(s[i +1]));
}
}
printf("\n");
}
While you already have a good answer, presuming that string s = get_string(); in the cs50.h world just fills s with a nul-terminated string, and s is either a character array or pointer to allocated memory there are a couple of areas where you may consider improvements.
First, don't use printf to print a single character. That is what putchar (or fputc) is for. (granted a smart optimizing compiler should do it for you, but don't rely on the compiler to fix inefficiencies for you) E.g., instead of
printf("%c", toupper(s[0]));
simply
putchar (toupper(s[0]));
Also, there are some logic issues you may wish to consider. What you want to know is (1) "Is the current character a letter?" (e.g. isalpha (s[x]), (2) "Is this the first character (e.g. index 0), or is it a character that follows a space?" (e.g. s[x-1] == ' '). With than information, you can use a single putchar to output the initials.
Further, with s being a string, you can simply use pointer arithmetic (e.g. while (*s) {.. do stuff with *s ..; s++;}) which ends when you reach the nul-terminator, or if you want to preserve s as a pointer to the first character, or if it is an array, then char *p = s; and operate on p)
Putting those pieces together, you could do something like the following without relying on string.h (you can use simple ifs and bit manipulations of the 6th bit to remove reliance on ctype.h functions as well -- that's for later):
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
int main (void) {
char *p = NULL;
printf ("Name: ");
string s = get_string(); /* assuming this works as it appears */
for (p = s; *p; p++)
/* if current is [a-zA-Z] and (first or follows space) */
if (isalpha (*p) && (p == s || (*(p - 1) == ' ')))
putchar (toupper (*p));
putchar ('\n'); /* tidy up */
return 0;
}
Example Use/Output
$ ./bin/initials
Name: David C. Rankin
DCR
$ ./bin/initials
Name: Jane Doe
JD
$ ./bin/initials
Name: d
D
$ ./bin/initials
Name: George W... Bush
GWB
Don't use strlen in the condition of the for-loop, it will be executed at every single step, better save the value in a variable and use the variable in the condition instead.
I would use in this case strtok, it deals with inputs like Tom marvolo riddle where you have multiple white spaces between the names.
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char line[1024];
fgets(line, sizeof line, stdin);
char *token, *src = line;
while(token = strtok(src, " \t"))
{
src = NULL; // subsequent calls of strtok must be called
// with NULL
printf("%c", toupper(*token));
}
printf("\n");
return 0;
}
When using strtok you have to remember not to pass a string literal ("this is a string literal") because they are read-only and strtok writes a \0 at the position where the delimiter is found. If you don't know if you have write access to buffer, you have to make a copy (either in a static buffer with enough length, or use malloc) and then use the copy in strtok.
In my example I know that line is not a read-only variable, thus I can safely use in strtok (provided that I won't use it any more, otherwise a copy is required).
One problem with strtok is that it is not reentrant and it's better to use strtok_r instead.
char *token, *src = line, *saveptr;
while(token = strtok_r(src, " \t", &saveptr))
...
In order to make your code work, a simple approach is to add a flag telling whether the previous character was a space. Something like:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
int wasSpace = 1; // Add a flag.
printf("Name: ");
string s = get_string();
for (int i = 0; i < strlen(s); i++)
{
if (wasSpace && s[i] != ' ') // Only print if previous was a space and this isn't
{
wasSpace = 0;
printf("%c", toupper(s[i]));
}
else if (s[i] == ' ')
{
wasSpace = 1; // Update the flag
}
}
printf("\n");
return 0;
}
I am trying to make this shell parse. How do I make the program implement parsing in a way so that commands that are in quotes will be parsed based on the starting and ending quotes and will consider it as one token? During the second while loop where I am printing out the tokens I think I need to put some sort of if statement, but I am not too sure. Any feedback/suggestions are greatly appreciated.
#include <stdio.h> //printf
#include <unistd.h> //isatty
#include <string.h> //strlen,sizeof,strtok
int main(int argc, char **argv[]){
int MaxLength = 1024; //size of buffer
int inloop = 1; //loop runs forever while 1
char buffer[MaxLength]; //buffer
bzero(buffer,sizeof(buffer)); //zeros out the buffer
char *command; //character pointer of strings
char *token; //tokens
const char s[] = "-,+,|, ";
/* part 1 isatty */
if (isatty(0))
{
while(inloop ==1) // check if the standard input is from terminal
{
printf("$");
command = fgets(buffer,sizeof(buffer),stdin); //fgets(string of char pointer,size of,input from where
token = strtok(command,s);
while (token !=NULL){
printf( " %s\n",token);
token = strtok(NULL, s); //checks for elements
}
if(strcmp(command,"exit\n")==0)
inloop =0;
}
}
else
printf("the standard input is NOT from a terminal\n");
return 0;
}
For an arbitrary command-line syntax, strtok is not the best function. It works for simple cases, where the words are delimited by special characters or white space, but there will come a time where you want to split something like this ls>out into three tokens. strtok can't handle this, because it needs to place its terminating zeros somewhere.
Here's a quick and dirty custom command-line parser:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int error(const char *msg)
{
printf("Error: %s\n", msg);
return -1;
}
int token(const char *begin, const char *end)
{
printf("'%.*s'\n", end - begin, begin);
return 1;
}
int parse(const char *cmd)
{
const char *p = cmd;
int count = 0;
for (;;) {
while (isspace(*p)) p++;
if (*p == '\0') break;
if (*p == '"' || *p == '\'') {
int quote = *p++;
const char *begin = p;
while (*p && *p != quote) p++;
if (*p == '\0') return error("Unmachted quote");
count += token(begin, p);
p++;
continue;
}
if (strchr("<>()|", *p)) {
count += token(p, p + 1);
p++;
continue;
}
if (isalnum(*p)) {
const char *begin = p;
while (isalnum(*p)) p++;
count += token(begin, p);
continue;
}
return error("Illegal character");
}
return count;
}
This code understands words separated by white-space, words separated by single or double quotation marks and single-character operators. It doesn't understand escaped quotation marks inside quotes and non-alphanumeric characters such as the dot in words.
The code is not hard to understand and you can extend it easily to understand double-char operators such as >> or comments.
If you want to escape quotation marks, you'll have to recognise the escape character in parse and unescape it and possible other escape sequences in token.
First, you've declared argv to be an array of pointers to... pointers. In fact, it is an array of pointers to chars. So:
int main(int argc, char **argv){
The trend is you want to reach for [], which got you into incorrect code here, but the idiom in C/C++ is more commonly to use pointer syntax, e.g.:
const char* s = "-+| ";
FWIW.
Also, note that fgets() will return NULL when it hits end of file (e.g., the user types CTRL-D on *nix or CTRL-Z on DOS/Windows). You probably don't want a segment violation when that happens.
Also, bzero() is a nonportable function (you probably don't care in this context) and the C compiler will happily initialize an array to zeroes for you if you ask it to (possibly worth caring about; syntax demonstrated below).
Next, as soon as you allow quoted strings, the next language question that immediately arises is: "how do I quote a quote?". Then, you are immediately out of the territory that can be handled cleanly with strtok(). I'm not 100% sure how you want to break your string into tokens. Using strtok() in the way you do, I think the string "a|b" would produce two tokens, "a" and "b", making you overlook the "|". You're treating "|" and "-" and "+" like whitespace, to be ignored, which is not generally what a shell does. For example, given this command-line:
echo 'This isn''t so hard' | cp -n foo.h .. >foo.out
I would probably want to get the following list of tokens:
echo
'This isn''t so hard'
|
cp
-n
foo.h
..
>
foo.out
Usually, characters like '+' and '-' are not special for most shells' tokenizing process (unlike '|' and '&' and '<', etc. which are instructions to the shell that the spawned command never sees). They get passed onto the application that is then free to decide "'-' indicates this word is an option and not a filename" or whatever.
What follows is a version of your code that produces the output I described (which may or may not be exactly what you want) and allows either double or single-quoted arguments (trivial to extend to handle back-ticks too) that can contain quote marks of the same kind, etc.
#include <stdio.h> //printf
#include <unistd.h> //isatty
#include <string.h> //strlen,sizeof,strtok
#define MAXLENGTH 1024
int main(int argc, char **argv[]){
int inloop = 1; //loop runs forever while 1
char buffer[MAXLENGTH] = {'\0'}; //compiler inits entire array to NUL bytes
// bzero(buffer,sizeof(buffer)); //zeros out the buffer
char *command; //character pointer of strings
char *token; //tokens
char* rover;
const char* StopChars = "|&<> ";
size_t toklen;
/* part 1 isatty */
if (isatty(0))
{
while(inloop ==1) // check if the standard input is from terminal
{
printf("$");
token = command = fgets(buffer,sizeof(buffer),stdin); //fgets(string of char pointer,size of,input from where
if(command)
while(*token)
{
// skip leading whitespace
while(*token == ' ')
++token;
rover = token;
// if possible quoted string
if(*rover == '\'' || *rover == '\"')
{
char Quote = *rover++;
while(*rover)
if(*rover != Quote)
++rover;
else if(rover[1] == Quote)
rover += 2;
else
{
++rover;
break;
}
}
// else if special-meaning character token
else if(strchr(StopChars, *rover))
++rover;
// else generic token
else
while(*rover)
if(strchr(StopChars, *rover))
break;
else
++rover;
toklen = (size_t)(rover-token);
if(toklen)
printf(" %*.*s\n", toklen, toklen, token);
token = rover;
}
if(strcmp(command,"exit\n")==0)
inloop =0;
}
}
else
printf("the standard input is NOT from a terminal\n");
return 0;
}
Regarding your specific request: commands that are in quotes will be parsed based on the starting and ending quotes.
You can use strtok() by tokenizing on the " character. Here's how:
char a[]={"\"this is a set\" this is not"};
char *buf;
buf = strtok(a, "\"");
In that code snippet, buf will contain "this is a set"
Note the use of \ allowing the " character to used as a token delimiter.
Also, Not your main issue, but you need to:
Change this:
const char s[] = "-,+,|, "; //strtok will parse on -,+| and a " " (space)
To:
const char s[] = "-+| "; //strtok will parse on only -+| and a " " (space)
strtok() will parse out whatever you have in the delimiter string, including ","
I'm trying to write a code that asks the user to enter a string and takes of all characters except the alphabetical.
Now i did it myself and it doesn't seem to work properly. I'm new to strings so i'm trying to understand and master strings. I tried to use gdb on mac but i don't have all the functions to understand this.
Could you please help?
What the code must do: User inputs (for example): h**#el(l)o&^w
and the output is hello.
here is my code:
#include <stdio.h>
#include <string.h>
int main()
{
char string[100];
int i;
int seen = 0;
printf("Enter String: ");
scanf("%s", string);
for (i=0; string[i]!='\0'; i++)
{
if (((string[i]<='a' || string[i]>'z')&&(string[i]<='A' || string[i]>'Z')) ||string[i]!='\0')
{
seen = 1;
}
else
seen = 0;
}
if (seen==0)
{
printf("%s", string);
}
}
well, your code has a couple of important problems:
you're not checking boundaries when iterating… what if I type in a 101 characters string? and a 4242 characters string?
next problem, is that scanf("%s", …) is considered dangerous, for the same reasons
so basically, what you'd want is to use fgets() instead of scanf().
But why not just get the input character by character, and build a string that has only the chars you want? It's simpler and flexible!
basically:
#include <ctype.h>
int main() {
char* string[100];
int i=0;
printf("Enter your string: ");
do {
// getting a character
char c = getchar();
// if the character is alpha
if (isalpha(c) != 0)
// we place the character to the current position and then increment the index
string[i++] = c;
// otherwise if c is a carriage return
else if (c == '\r') {
c = getchar(); // get rid of \n
// we end the string
string[i] = '\0'
}else if (c == '\n')
// we end the string
string[i] = '\0';
// while c is not a carriage return or i is not out of boundaries
} while (c != '\n' || i < 100);
// if we've got to the boundary, replace last character with end of string
if (i == 100)
string[i] = '\0';
// print out!
printf("Here's your stripped string: %s\n", string);
return 0;
}
I did not run it on my computer because it's getting late, so my apologies in case of mistakes.
Addendum:
wee the program skips my statement and shuts down
that's because your condition is inversed, and remove the \0 condition, as it will always happen with the scanf() that always append \0 to the string to end it. Try exchanging seen = 1 and seen = 0 or try using the following condition:
if ((string[i]>='a' && string[i]<='z')||(string[i]>='A' && string[i]<='Z')))
seen = 1;
else
seen = 0;
or simply, use ctypes's isalpha() function, like in our two examples!
No part(remove the extra characters) to change the string in your code.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
char *filter(char *string, int (*test)(int)) {
char *from, *to;
for(to = from = string;*from;++from){
if(test(*from))
*to++ = *from;
}
*to = '\0';
return string;
}
int main(){
char string[100];
printf("Enter String: ");
scanf("%99s", string);
printf("%s\n", filter(string, isalpha));
return 0;
}