1

My exercise requires that I can use case-insensitive input. My approch is that I use the tolower and toupper function.

How can I convert the array to lowercase letters?

void KULstrcichr(char *arr, char search)
{
    printf("Return value when uppercase character %c is passed to isupper(): %d\n", search, isupper(search));
    // The  strchr() function returns a pointer to the first occurrence of the character c in the string s.
    if (isupper(search))
    {
        printf("Groß\n");
        char lowercasesearch = tolower(search);
        printf("Das ist der Output: %s", arr);
        char *ptr = strchr(arr, lowercasesearch);
        printf("Das ist der Buchstabe: %s", ptr);
    }
    else
    {
        printf("Klein\n");
        char upercasesearch = toupper(search);
        printf("Das ist der Output: %s", arr);
        char *ptr = strchr(arr, upercasesearch);
        printf("Das ist der Buchstabe: %s", ptr);
    }
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 3
    Did you post this exact question earlier today? Because I remember seeing the exact same title before. If you did it, then don't delete and repost questions, [edit] the ones you have to improve it instead. – Some programmer dude May 03 '22 at 16:15
  • C is not designed to work with Unicode text out of box. Your `ß` here is *not*, from C's perspective, Unicode code point U+00DF; it is an *8-bit integral value* that *incidentally happens* to equal 0xDF. Unless `char` on your platform is signed, which it legally may or may not be (and regardless of which it is, `char` is a separate, still numeric, type from both `signed char` and `unsigned char`). That said `tolower` and `toupper` work on individual `char` values, and will give you a numeric result (the return type is `int` so that `-1` can be returned for errors). – Karl Knechtel May 03 '22 at 16:18
  • @Someprogrammerdude I also recall seeing such a question, but this version looks much higher quality. Reposting questions is reasonable behaviour when the question basically needs to be rewritten from scratch; no reason the new version should be saddled with the old downvotes. – Karl Knechtel May 03 '22 at 16:19
  • 1
    There was a question "I've no idea how to implement a case-insensitive version of `strchr()`" earlier. There was no attempt to solve the problem. This is much better; at least it shows a little thought and code. – Jonathan Leffler May 03 '22 at 16:20
  • 1
    @KarlKnechtel "`tolower` and `toupper` work on individual `char` values" is more like "... on individual `unsigned char` values". – chux - Reinstate Monica May 03 '22 at 18:19
  • 1
    Anyway, part of the issue here is thinking about the problem logically. Simply converting the *input `char`* to lowercase (or uppercase) will not make a case-insensitive search possible. The point is to be able to *compare* `char`s that are in *the same* case. The search string potentially contains characters in mixed case; so logically, you need to *normalize* each value to the same case *as you proceed* through the comparison. – Karl Knechtel May 03 '22 at 18:23
  • 1
    However, it is not useful or desired to change the data within the search string (the calling code will be surprised, since this code was only supposed to do a comparison, not modify its data). Instead, fix the case *as you retrieve* individual values from the array for comparison purposes. You are being asked to implement the internal logic yourself, *not* to prepare for a call to `strchr`. – Karl Knechtel May 03 '22 at 18:25

5 Answers5

4

According to the title of the question

Make own strchr() function but case-insensitive

your code does not make any sense. You should write your own function similar to strchr that is declared in the C Standard like

char * strchr(const char *s, int c);

The function should be declared and defined the following way

char * my_strchr( const char *s, int c )
{
    c = tolower( ( unsigned char )c );

    while ( *s && tolower( ( unsigned char ) *s ) != c ) ++s;

    return c == '\0' || *s != '\0' ? ( char * )s : NULL;
}

Here is a demonstration program.

#include <stdio.h>
#include <ctype.h>

char *my_strchr( const char *s, int c )
{
    c = tolower( ( unsigned char )c );

    while (*s && tolower( ( unsigned char )*s ) != c) ++s;

    return c == '\0' || *s != '\0' ? ( char * )s : NULL;
}

int main( void )
{
    char s[] = "Hello";
    char *p = my_strchr( s, 'h' );

    if (p != NULL)
    {
        printf( "position = %td, substring = %s\n", p - s, s );
    }
}

The program output is

position = 0, substring = Hello
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • @chux-ReinstateMonica In the question the second parameter has the type char. – Vlad from Moscow May 03 '22 at 18:54
  • I have a problem using this code with my input. I'am sending an array with a string, and character with the the letter I'am searching for. (Hello, H) I'am getting only erros trying to get this code running. I don't understand the return code can you explan it? – TheLuckyGuy May 04 '22 at 08:04
  • @TheLuckyGuy See my appended answer with a demonstration program. – Vlad from Moscow May 04 '22 at 09:29
1

"How can I convert the array to lowercase letters?" - you don't. Instead, check character by character until you've reached the end of the string.

  • First convert the char you search for to lower (or upper) case.
    char lowsearch = tolower((unsigned char)search);
    
  • Then loop arr until *arr == '\0' and check each character on the way:
    for(;*arr != '\0'; ++arr) {
        if(tolower((unsigned char)*arr) == lowsearch) return arr;
    }
    return NULL;
    

Note that this requires that you return a char* just like strchr, not void

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
1

How can I convert the array to lowercase letters? void KULstrcichr(char *arr, char search)

  • To achieve "Make own strchr() function but case-insensitive", use an interface like char *strchr(const char *s, int c); for maximum compatibility with that standard function.

  • Convert the search and *arr to a common case. e.g. tolower(). tolower(int ch) is defined for a ch in the unsigned char range and EOF.

Example:

char *TLG_strchr(const char *s, int c) {
  c = tolower((unsigned char)c);

  // str...() functions perform as if `s` was `unsigned char *`.
  const unsigned char *us = (const unsigned char *) s;

  while (tolower(*us) != c && *us) {
    us++;
  }

  return tolower(*us) == c ? (char *) us : NULL;
}

Pedantic: (unsigned char)*s is incorrect for rare non-2's compliment machines with signed char. Better as *((unsigned char *)s), which is effectively what this answer does, to properly access negative char and distinguish +0 from -0. This likely will be a non-issue for the next version of C as that is expected to require 2's compliment.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

This little assignment is more tricky than it looks:

  • your code does not always work because you only search either for uppercase or for lowercase, depending on the case of search, which does not cover all cases (pun intended).

  • using strchr first with uppercase, then with lowercase if uppercase was not found is still incorrect: you must find the first match for either case.

  • you must also find the null terminator if search is 0. Simple implementations usually fail this test.

  • tolower and toupper are not defined for negative values different from EOF, hence they should not be passed char values that can be negative on platforms where char is signed by default. Casting the char argument as (unsigned char) is a simple way to avoid this issue.

Here is an example:

// using int c for compatibility with char *strchr(const char *s, int c)
char *KULstrcichr(const char *s, int c) {
    int uc = tolower((unsigned char)c);

    for (;; s++) {
        if (tolower(*(unsigned char *)s) == uc)
            return (char *)s;
        if (*s == '\0')
            return NULL;
    }
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
0

Since it looks like you're using simple 8-bit ASCII characters, note that a given uppercase character is just (lowercase & 0x20). Take the input character and run it through strchr() twice: once with a lowercase version, and once with an uppercase version.

char *myStrchr(const char *s, char c) {
    char *pLower;
    char *pUpper;
    char *p;

    c |= 0x20;
    pLower = strchr(s, c);
    c &= ~0x20;
    pUpper = strchr(s, c);
    if (pLower == NULL) {
        p = pUpper;
    } else if (pUpper == NULL) {
        p = pLower;
    } else {
        p = (pLower < pUpper) ? pLower : pUpper;
    }
    return p;
}

Note: I don't think you want your function to be of type void.

EDIT: if your input string may contain characters other than letters, you'll need to bound-check before performing the searches.

mzimmers
  • 857
  • 7
  • 17
  • 4
    Your code will search for `'@'` and then ```'`'``` — because you 'case-convert' every character, rather than only case-converting letters. You should also run the second search with letters, and return the earlier of the two occurrences. For example, if case-insensitively searching for `t` in `The first letter T is wanted`, the first search would find the `t` in `first`, but the second would find the `T` in `The`, and that's the match that should be returned. – Jonathan Leffler May 03 '22 at 16:34
  • I already noted your first point, but your second point is valid. I'll edit my answer...thanks. – mzimmers May 03 '22 at 16:37
  • @mzimmers You are probably right but what made you draw this conclusion? _"it looks like you're using simple 8-bit ASCII characters"_ – Ted Lyngmo May 03 '22 at 16:43
  • @TedLyngmo: it's more of an assumption than a conclusion, but in my experiences, chars are generally 8-bit values in C. – mzimmers May 03 '22 at 16:49