Thread: strings check problem

  1. #1
    Registered User
    Join Date
    Jul 2014
    Posts
    4

    strings check problem

    Hello guys (newbie here),
    My problem stands for strings. Somehow, I can't check properly if the user entered the correct answer when is asked about unit (wh, kwh and mwh). Thanks!

    Code:
    if(choose == 'w') {
                printf("Choose unit (wh, kwh, mwh):");
                fflush(stdin);
                fgets(unit,4,stdin);
                for (i = 0; unit[i]; i++){
                    unit[i] = tolower(unit[i]);
                    }
                printf("Enter the value:"); //sa verifici daca introduce altceva decat decimal sau float
                fflush(stdin);
                scanf("%f",&whour);
                
    //problem HERE!--------------------------------
                for(;;){
                    
                    if (unit == "wh"){
                        result = whour * 3600;
                        break;
                    } else if (unit == "kwh"){
                        result = (whour*1000) * 3600;
                        break;
                    } else if (unit=="mwh"){
                        result = (whour*1000000)* 3600;
                        break;
                    } else {
                        printf("Wrong!");
                        printf("Choose unit (wh, kwh, mwh):");
                        fflush(stdin);
                        fgets(unit,4,stdin);
                        for (i = 0; unit[i]; i++){
                        unit[i] = tolower(unit[i]);
                    }
                    }
                }
    //problem  Here! --------------------------------------

  2. #2
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Don't use fflush(stdin). Its behavior is undefined.

    You cannot compare strings with == in C. You need to use strcmp. Google it.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  3. #3
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    fgets will read (if there is space) '\n' into the buffer
    so comparing unit to "wh" will fail till you get rid of \n
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  4. #4
    Registered User zub's Avatar
    Join Date
    May 2014
    Location
    Russia
    Posts
    104
    I suggest to turn once the string to a number and then compare the numbers. This is called hashing. For such short strings even perfect hashing (when two different strings never generate the same hash) is trivial.
    Our goals are clear, tasks are defined! Let's work, comrades! -- Nikita Khrushchev

  5. #5
    Registered User
    Join Date
    Jul 2014
    Posts
    4
    Many many thanks! Like always, very helpful! Right now, I'm trying all of these options.

    Zub, first I used numbers to select options, however, somehow if the input required is a number and the user enters a char, at some specific letters (don't remember witch of them), the program will run (Am i wrong if I say that is because of the ASCII code of the letters?).

  6. #6
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    Quote Originally Posted by Ionut Achim View Post
    ...
    Zub, first I used numbers to select options, however, somehow if the input required is a number and the user enters a char, at some specific letters (don't remember witch of them), the program will run (Am i wrong if I say that is because of the ASCII code of the letters?).
    I guess that depends on the format specifiers you are using when inputting an option ("%c" or "%d") and the data-type of the option (int or char).

    Anyway, zub's suggestion for hashing a string to an int (I guess for efficiency reasons later on in the program) which in your case can be done with perfect-hashing (as also suggested by zub), is a nice option imho, because it will open possibilities for improving your code.

    If you want to give another chance in reading ints instead of strings, then the reverse kind of the suggested hashing (int to string) may also prove useful in the long run. With a little extra code for hard-coding the hash-table and a couple of supporting code (an enum and a validation macro) you can have something like this:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    #define VALID_UNIT(u)  ( (u) > UNIT_NONE && (u) < MAX_UNITS )
    enum {
        UNIT_NONE = -1,
        UNIT_WH   = 0,
        UNIT_KWH,
        UNIT_MWH,
    
        /* not a unit, just their total count */
        MAX_UNITS
    };
    
    ...
    
    int main( void )
    {
        int    unit = UNIT_NONE;
        char   *labels[MAX_UNITS] = { "wh", "kwh", "mwh" };
    
        ...
    You can then use the enumerated values for indexing the labels array and getting the corresponding string, if for some reason you want to handle units as strings instead of int... e.g. for printing them in human readable form.

    In such a case, you would input the unit variable from the user, validate it, and then print the label that corresponds to that unit...

    Code:
    printf( "Choose unit: " );
    if ( 1 == scanf("%d", &unit) && VALID_UNIT(unit) ) {
        printf( "you chose \"%s\"\n", labels[unit] );
    }
    Actually, the enumerated values may now be used for indexing any array related to your units. For your example, you could define a multipliers array, in parallel with the labels array... something like this:

    Code:
    ...
    
    int main( void )
    {
        int    unit = UNIT_NONE;
        char   *labels[MAX_UNITS] = { "wh", "kwh", "mwh" };
        double multipliers[MAX_UNITS] = { 1.0, 1000.0, 1000000.0 };
        ...
    so that when the user inputted unit var has been successfully validated, along with the whour var, you can calculate your result directly:

    Code:
    ...
            result = 3600.0 * ( whour * multipliers[unit] );
    ...
    If you are up to it, you could also warp the labels & multipliers arrays into a struct, or if you are not very fond of parallel-arrays, you could define a struct with a string and a double field, and then define an array of that struct.

    Parallels arrays are just fine for getting you started though, imho.

    On another note, regarding fflush( stdin ). As already pointed out by Elkvis, this happens to work on Windows, but according to the C standards it produces undefined behavior. If you try it on other platforms, you'll see that on most of them it is not working.

    It has to do with the buffered nature of stdin, and a somewhat quick & dirty solution (although not full proff) is to use fgets() along with sscanf() when reading from stdin, using a custom buffer in between.

    For example...

    Code:
    #include <stdio.h>   /* for BUFSIZ among other things */
    
    int main( void )
    {
        char input[BUFSIZ] = {'\0'};
        double d = .0;
    
        if ( NULL == fgets( input, BUSIZ, stdin )
        || 1 != sscanf( input, "%lf", &d )
        ){
            fputs( "*** error, bye...\n", stderr );
            return 1;
        }
    
        printf( "You entered: %f\n", d );
        return 0;
    }
    The user can still mess you up if he/she overflows your input buffer in stdin (you can prevent that too, but the code would be even more cumbersome).

    So, all in all... the code-snippet you presented in the 1st post, converted to a small program using what I'm trying to demontsrate in this post, would look something like the following...

    Code:
    #include <stdio.h>
    
    #define VALID_UNIT(u)  ( (u) > UNIT_NONE && (u) < MAX_UNITS )
    enum {
        UNIT_NONE = -1,
        UNIT_WH   = 0,
        UNIT_KWH,
        UNIT_MWH,
    
        /* not a unit, just their total count */
        MAX_UNITS
    };
    
    /* ------------------------------------------
     *
     * ------------------------------------------
     */
    void units_print_menu( char *labels[MAX_UNITS] )
    {
        int i;
    
        if ( NULL == labels ) {
            return;
        }
    
        puts( "Choose unit" );
        for (i=0; i < MAX_UNITS; i++) {
            if ( NULL == labels[i] ) {
                fprintf(
                    stderr,
                    "\n%s: NULL element found (labels[%d])\n",
                    __func__,
                    i
                    );
                return;
            }
            printf( "%d. %s\n", i, labels[i] );
        }
        printf( "> " );
        fflush( stdout );
    }
    
    /* ------------------------------------------
     *
     * ------------------------------------------
     */
    int main( void )
    {
        char   input[BUFSIZ] = {'\0'};
        int    unit = UNIT_NONE;
        double whour = 0;
        double result = 0;
        char   *labels[MAX_UNITS] = { "wh", "kwh", "mwh" };
        double multipliers[MAX_UNITS] = { 1.0, 1000.0, 1000000.0 };
    
        for (;;) {
    
            units_print_menu( labels );
            if ( NULL == fgets( input, BUFSIZ, stdin )
            || sscanf( input, "%d", &unit ) < 1
            || !VALID_UNIT( unit )
            ){
                puts( "*** invalid unit or error, try again...\n" );
                continue;
            }
    
            putchar( '\n' );
            printf( "Enter the value: " );
            if ( NULL == fgets( input, BUFSIZ, stdin)
            || sscanf( input, "%lf", &whour) < 1
            ){
                puts( "*** invalid whour or error, start over...\n" );
                continue;
            }
    
            result = 3600.0 * ( whour * multipliers[unit] );
            break;
        }
    
        printf( "%f\n", result );
    
        return 0;
    }
    Last edited by migf1; 08-15-2014 at 02:45 AM. Reason: damn typos ( all over the place :P )

  7. #7
    Registered User zub's Avatar
    Join Date
    May 2014
    Location
    Russia
    Posts
    104
    Code:
    /* My variation of Bernstein algorithm:
    1. Perfect hashing for a string consist of no more than 6 symbols of the same type.
       For example, alphabetic only or numeric only, but not alphanumeric.
    2. Not bad hashing for longer strings.
    3. Mixing of different symbols (alphanumeric) may provoke more frequent collisions.
    4. Case-insensitive.
    */
    
    
    unsigned long hash(const char* str)
    {
        unsigned long h = 0;
        unsigned long c;
        while( c = (unsigned char) *str++ ) {
            c &= 31;
            h *= 33;
            h += c;
        }
        return h;
    }
    
    
    double multiplier(const char* const str)
    {
        static const mult_1 = hash("wh");
        static const mult_1000 = hash("kwh");
        static const mult_1000000 = hash("mwh");
        switch( hash(str) ) {
            case mult_1: return 1.0;
            case mult_1000: return 1000.0;
            case mult_1000000: return 1000000.0;
        }
        return 0.0;
    }
    Our goals are clear, tasks are defined! Let's work, comrades! -- Nikita Khrushchev

  8. #8
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    I wish to stress that all hashing involves something called a hash function. Methods that do not use such a function are not hashing, despite claims to the contrary. Zub has it right.

  9. #9
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    Quote Originally Posted by whiteflags View Post
    I wish to stress that all hashing involves something called a hash function. Methods that do not use such a function are not hashing, despite claims to the contrary. Zub has it right.
    If I get this right, you are suggesting that this for example...

    Code:
    #define VALID_UNIT(u)  ( (u) > UNIT_ERR && (u) < MAX_UNITS )
    enum {
        UNIT_ERR = 0,
        UNIT_WH  = 1,
        UNIT_KWH,
        UNIT_MWH,
     
        /* not a unit, just their total count */
        MAX_UNITS
    };
    
    int hash( int key )
    {
        return VALID_UNIT(key) ? key : UNIT_ERR;
    }
    
    int main( void )
    {
        int unit = UNIT_ERR;
        char *labels[MAX_UNITS] = { "#ERROR", "wh", "kwh", "mwh" };
    
        printf( "Choose unit: " );
        scanf( "%d", &unit );
        printf( "you chose \"%s\"\n", labels[ hash(unit) ] );
    
        return 0;
    }
    involves hashing, but the code I posted in the previous post, which uses array-indexing directly instead of repeating itself into a hash function, does not involve hashing?

    I can understand that the term "hashing" may be a bit of a stretch for this case, but I think you are way too picky for no apparent reason.
    "Talk is cheap, show me the code" - Linus Torvalds

  10. #10
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    Code:
                printf("Choose unit (wh, kwh, mwh):");
                fflush(stdin);
                fgets(unit,4,stdin);
    For some reason I have seen this a few times: You probably want fflush(stdout), because the printf does not end in a new line character

    Code:
                printf("Choose unit (wh, kwh, mwh):");
                fflush(stdout);
                fgets(unit,4,stdin);
    A buffer only has to be flushed when the buffer is full, or a new line is detected.
    C99 7.19.3 #3 (I have split the paragraphs for readability)
    When a stream is unbuffered, characters are intended to appear from the source or at the destination as soon as possible. Otherwise characters may be accumulated and transmitted to or from the host environment as a block.

    When a stream is fully buffered, characters are intended to be transmitted to or from the host environment as a block when a buffer is filled. When a stream is line buffered, characters are intended to be transmitted to or from the host environment as a block when a new-line character is encountered.

    Furthermore, characters are intended to be transmitted as a block to the host environment when a buffer is filled, when input is requested on an unbuffered stream, or when input is requested on a line buffered stream that requires the transmission of characters from the host environment.

    Support for these characteristics is implementation-defined, and may be affected via the setbuf and setvbuf functions.
    This being said, most hosted environments will flush stdout without the new line.

    I have heard of problems with UNIX, so it is worth keeping in mind.
    Fact - Beethoven wrote his first symphony in C

  11. #11
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by migf1 View Post
    I can understand that the term "hashing" may be a bit of a stretch for this case, but I think you are way too picky for no apparent reason.
    I did not actually correct you for your benefit.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by migf1
    If I get this right, you are suggesting that this for example...
    Not likely. Your function named hash performs validation (via a macro that contains the actual validation code), not hashing.

    Quote Originally Posted by migf1
    involves hashing, but the code I posted in the previous post, which uses array-indexing directly instead of repeating itself into a hash function, does not involve hashing?
    From what I see, in your code from post #6, the user is required to enter the unit as an integer. Therefore, there is no mapping from key to value, thus there is no hash function needed, i.e., it does not involve hashing. The value is used as-is after validation.

    Quote Originally Posted by migf1
    I can understand that the term "hashing" may be a bit of a stretch for this case, but I think you are way too picky for no apparent reason.
    There is a pretty good reason from what I see: in post #1, the user enters the unit as a string, and indeed zub's suggestion of hashing in post #4 operates on that assumption.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    Quote Originally Posted by laserlight View Post
    Not likely. Your function named hash performs validation (via a macro that contains the actual validation code), not hashing.

    From what I see, in your code from post #6, the user is required to enter the unit as an integer. Therefore, there is no mapping from key to value, thus there is no hash function needed, i.e., it does not involve hashing. The value is used as-is after validation.

    There is a pretty good reason from what I see: in post #1, the user enters the unit as a string, and indeed zub's suggestion of hashing in post #4 operates on that assumption.
    Yes, I believe I was explicit when I was writing:

    If you want to give another chance in reading ints instead of strings, then the reverse kind of the suggested hashing (int to string) may also prove useful in the long run. With a little extra code for hard-coding the hash-table and a couple of supporting code (an enum and a validation macro) you can have something like this...
    since the OP had already stated that first he tried reading ints instead of strings, but he had problems and decided to try reading strings instead (and had problems again).

    As for the hash() function, if we are to play "who is more picky" guys, then I'm not familiar with any definition that mandates the way any hash function is implemented.

    I also very much doubt that there is a strict definition at all.
    For example, according to this definition...

    Hash function

    A hash function is any well-defined procedure or mathematical function that converts a large, possibly variable-sized amount of data into a small datum, usually a single integer that may serve as an index to an array (cf. associative array). The values returned by a hash function are called hash values, hash codes, hash sums, checksums or simply hashes.
    ...
    my redundant hash() function of the previous post, does indeed map a large set amount of data (the int range) into a very small data, (an int of a much smaller range) that is used as an index to an array. Thus, I don't see why it is not qualified as a hash function.

    PS. Btw, I don't think that excessive pickiness is in any way constructive.
    "Talk is cheap, show me the code" - Linus Torvalds

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by migf1
    I'm not familiar with any definition that mandates the way any hash function is implemented.
    There isn't, but not all mapping functions are hash functions. Like "reverse kind of the suggested hashing (int to string)" is just a mapping, not hashing.

    Quote Originally Posted by migf1
    my redundant hash() function of the previous post, does indeed maps a large set amount of data (the int range) into a very small data, (an int) that is used as an index to an array. Thus, I don't see why it is not qualified as a hash function.
    Because it is performing validation, not hashing. You aren't mapping from a valid key to a valid value. Rather, you are checking if a key is invalid, and if so, the result is a value that is used to denote an error, otherwise the valid key is returned as-is. Consequently, your function is not "redundant": it is important because validation is important, but at the same time it isn't hashing. So yes, indeed the function maps a large range into a small range, and indeed the result, including the one that denotes an error, is used as an array index, but it is not the same concept, and portraying it as such does a disservice to learners.

    Instead of saying hashing, one could just say: print a menu such that the units are numbered. The user will enter the number corresponding to the unit. Check that the number entered is valid. Here's how you might handle the validation and matching of the number entered to the unit name... (insert code example here).

    Quote Originally Posted by migf1
    I don't think that excessive pickiness is in any way constructive.
    Discussion of important terminology is not excessive and is constructive.
    Last edited by laserlight; 08-17-2014 at 06:16 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    If you want to give another chance in reading ints instead of strings, then the reverse kind of the suggested hashing (int to string) may also prove useful in the long run. With a little extra code for hard-coding the hash-table and a couple of supporting code (an enum and a validation macro) you can have something like this...
    since the OP had already stated that first he tried reading ints instead of strings, but he had problems and decided to try reading strings instead (and had problems again).
    You were never promoting hashing. Your new method is not hashing, either. I consider the bare minimum of hashing to be reinterpreting the key so that it maps to a value.

    This is pulled from the wikipedia article I linked, on trival hashing (the absolute bare minimum).
    If the datum to be hashed is small enough, one can use the datum itself (reinterpreted as an integer) as the hashed value. The cost of computing this "trivial" (identity) hash function is effectively zero. This hash function is perfect, as it maps each input to a distinct hash value.
    You are not even using the value you will find as the key, the case where there is no hash to perform. You are not hashing.

    PS. Btw, I don't think that excessive pickiness is in any way constructive.
    I am being difficult because you confused data structures, and if the OP comes back and reads the thread, he would walk away with a wrong idea of what hashing is. Feel free to be annoyed by me, but I did not correct you for your benefit.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 5
    Last Post: 02-24-2009, 05:29 PM
  2. Quick check on reading in + using strings
    By advancedk in forum C Programming
    Replies: 2
    Last Post: 12-08-2008, 10:12 PM
  3. Check strings which should only contain some characters.
    By qingxing2005 in forum C Programming
    Replies: 2
    Last Post: 06-17-2008, 09:32 AM
  4. Please help :( problem on check spelling...
    By toxicherry in forum C Programming
    Replies: 4
    Last Post: 12-29-2007, 12:47 AM
  5. Error check problem again
    By rtransfi in forum C Programming
    Replies: 6
    Last Post: 02-27-2003, 04:55 PM