0

I am having issue with lower casing my words that are being used as inputs. So my program takes in words and sorts them alphabetically and removes duplicates. But I'd like to change words upper case and lower them to equal to lower case words.

example: Apple changes to apple

my input:

./a.out Orange apple banana Apple banana

my output:

Apple
Orange
apple
banana

Here is what I am trying to achieve

output:

apple
banana
orange

Here is my code

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
  int i, j, k, size;
  size = argc -1;
  char *key;
  char* a[argc-1];

  for (i = 2; i < argc; i++) {
    key = argv[i];

    j = i-1;
    while (j >= 1 && strcmp(argv[j], key) > 0) {
      argv[j+1] = argv[j];
      j--;
    }

    argv[j+1] = key;
  }

  if (argc > 1){
      for (i = 1; i < argc;){
        puts(argv[i]);

        while (argv[++i] != NULL && strcmp(argv[i - 1], argv[i] ) == 0)
          continue;
      }
  }

  return 0;
}
iMataMi
  • 69
  • 2
  • 11
  • Do you know about `ctype.h`? – Mad Physicist Nov 22 '16 at 19:35
  • http://stackoverflow.com/questions/23618316/undefined-reference-to-strlwr – BLUEPIXY Nov 22 '16 at 19:36
  • @MadPhysicist Nope. Just a C beginner. – iMataMi Nov 22 '16 at 19:37
  • @MadPhysicist I see that `strlwr` is part of `type.h` I am not sure if i am allowed to use that. I was hoping for an idea on how to approach it while I'm working with pointers. @BLUEPIXY – iMataMi Nov 22 '16 at 19:38
  • *I am having issues* is only useful as a problem description if you explain what **issues** you're having. You've not explained a problem with the code you posted. What **specific question** do you have for us? – Ken White Nov 22 '16 at 19:43
  • @sleep-elite. There is no need for pointer fanciness here, but I'm sure you can find a way to work it in. – Mad Physicist Nov 22 '16 at 19:44
  • @KenWhite. Did *you* read the question? – Mad Physicist Nov 22 '16 at 19:44
  • 1
    @MadPhysicist: Yes, Did you? It says *I am trying to do this* and dumps some code. It does not mention a problem with the code, what the issue is with the code, or ask a question at all related to the code. Where do you see a description of a problem? *I have an issue* is not a problem description. As I asked the poster, **what is the issue**? Did **you** note that the question is related to the use of pointers, presumably as a homework assignment, and thus the poster is not looking for ctypes? – Ken White Nov 22 '16 at 19:47
  • 1
    Actually, it analyzes the expected and actual results, with a very clear problem statement before dumping the code. The problem is "I did a successful comparison, but do not know how to do it case-insensitively". Obviously the question is homework related, but the OP did 90% of the work and is stuck on a small detail. And keep in mind that `argv` is a pointer to an array of pointers. – Mad Physicist Nov 22 '16 at 19:52
  • 2
    @KenWhite Relax. They gave inputs and outputs and a description. It looks like they want to downcase, sort, and only output uniques. – Schwern Nov 22 '16 at 19:53

3 Answers3

3

You have a list of words and you want to output them sorted, and only the unique ones. And you want to do it in a case insensitive fashion.

  1. Get all the strings to the same case.
  2. Sort the list of strings.
  3. Don't output repeats.

C has no built in function to lower case a string, but it does have ones to lower case characters: tolower. So we write a function to lower case a whole string by iterating through it and lower casing each character.

void str_lower(char *str) {
    for( ; str[0] != NULL; str++ ) {
        str[0] = (char)to_lower(str[0]);
    }
}

Then we need to sort. That's handled by the built in qsort function. To use it, you need to write a function that compares two strings and returns just like strcmp. In fact, your comparison function will just be a wrapper around strcmp to make qsort happy.

int compare_strings( const void *_a, const void *_b ) {
    /* The arguments come in as void pointers to the strings
       and must be cast. Best to do it early. */
    const char **a = (const char **)_a;
    const char **b = (const char **)_b;

    /* Then because they're pointers to strings, they must
       be dereferenced before being used as strings. */
    return strcmp(*a, *b);
}

In order to handle any data type, the comparison function takes void pointers. They need to be cast back into char pointers. And it's not passed the string (char *) it's passed a pointer to the string (char **), again so it can handle any data type. So a and b need to be dereferenced. That's why strcmp(*a, *b).

Calling qsort means telling it the array you want to sort, the number of items, how big each element is, and the comparison function.

qsort( strings, (size_t)num_strings, sizeof(char*), compare_strings );

Get used to this sort of thing, you'll be using it a lot. It's how you work with generic lists in C.


The final piece is to output only unique strings. Since you have them sorted, you can simply check if the previous string is the same as the current string. The previous string is strings[i-1] BUT be sure not to try to check strings[-1]. There's two ways to handle that. First is to only do the comparison if i < 1.

for( int i = 0; i < num_strings; i++ ) {
    if( i < 1 || strcmp( strings[i], strings[i-1] ) != 0 ) {
        puts(strings[i]);
    }
}

Another way is to always output the first string and then start the loop from the second.

puts( strings[0] );
for( int i = 1; i < num_strings; i++ ) {
    if( strcmp( strings[i], strings[i-1] ) != 0 ) {
        puts(strings[i]);
    }
}

This means some repeated code, but it simplifies the loop logic. This trade-off is worth it, complicated loops mean bugs. I botched the check on the first loop myself by writing if( i > 0 && strcmp ... )`.


You'll notice I'm not working with argv... except I am. strings and num_strings are just a bit of bookkeeping so I didn't always have to remember to start with argv[1] or use argv+1 if I wanted to pass around the array of strings.

char **strings = argv + 1;
int num_strings = argc-1;

This avoids a whole host of off-by-one errors and reduces complexity.


I think you can put the pieces together from there.

Schwern
  • 153,029
  • 25
  • 195
  • 336
0

There are a set of standard functions for checking and changing the type of characters in ctype.h. The one you are interested in is tolower(). You can #include<ctype.h> and then add a snippet like the following to pre-process your argv before doing the sorting:

for(i = 1; i < argc; i++) {
    argv[i][0] = tolower(argv[i][0]);
}

That will only operate on the first character of each word. If you need to normalize the entire word:

for(i = 1; i < argc; i++) {
    for(j = 0; argv[i][j]; j++) {
        argv[i][j] = tolower(argv[i][j]);
    }
}
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
0

Silly me, I was able to figure it out after looking at my code realizing that i can do key[0] = tolower(key[0]); which i did before having a pointer point at it.

for (i = 2; i < argc; i++) {
    key = argv[i];
    key[0] = tolower(key[0]);
    j = i-1;
    while (j >= 1 && strcmp(argv[j], key) > 0) {
      argv[j+1] = argv[j];
      j--;
    }

    argv[j+1] = key;
  }

Which lower cases the first letter. And if i wanted to lower case all the letters, i would've have used a for loop. Thank you everyone for your contribution. :)

iMataMi
  • 69
  • 2
  • 11