0

In my problem I am trying to read a bunch strings separated by new line '\n', and find the longest and shortest strings. The problem is that I don't know how to find the shortest one. Is there a way to set array min to something big?

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

int main(void) {
    char c;
    int i = 0, j = 0, n;
    char input[100] = { NULL }, max[100] = { NULL }, min[100] = { NULL };

    while ((c = getchar()) != EOF) {
        input[i++] = c;
        if (c == '\n') {
            if (strlen(input) > strlen(max)) {
                strcpy(max, input);
            }

            if (strlen(input) < strlen(min)) {
                strcpy(min, input);
            }

            memset(input, NULL, 100);
            i = 0;
        }
    }

Edit: How do I output only one(of the) value when the input is only one line?
Ex. input: I love hot girls\n
^Z
output:
max: I love hot girls
min:

5 Answers5

1

There are many small problems in your code:

  • c should be declared as int to allow proper detection of EOF.
  • you include the newline in the line length, which may not be appropriate.
  • char input[100] = { NULL } is not a proper initializer for a char array: NULL is a macro for the null pointer, not the null character. Your code may compile without warnings if NULL is defined as 0, but not if it is defined as ((void*)0), which is a more common standard option in C. You should instead use this syntax:

    char input[100] = "", ...
    
  • for the same reason, memset(input, NULL, 100); should be written memset(input, 0, 100); or memset(input, '\0', 100); or even better:

    memset(input, '\0', sizeof(input));
    
  • you do not test if i is less than the size of the input array. Storing the byte into input[i] has undefined behavior beyond 99.

  • you do not need strlen() to compute the length of input, i is the length so far. You can also keep the length of min and max as separate variables, and avoid rescanning these arrays.

As for your question: Is there a way to set array min to something big? There are 3 possible approaches:

  • you can initialize the minimum length to INT_MAX defined in <limits.h> or SIZE_MAX if you make i a size_t.
  • you can initialize the minimum length to 100 since the maximum line length you can handle is 99, but you would still need to special case the empty input file.
  • you can special case the first line, which is the best approach, compatible with an empty input file.

Here is the modified code:

#include <stdio.h>
#include <strlen.h>

#define LINE_SIZE  100

int main(void) {
    char input[LINE_SIZE] = "", max[LINE_SIZE] = "", min[LINE_SIZE] = "";
    size_t i, lmin, lmax;
    size_t linenum = 0;

    for (i = lmin = lmax = 0;;) {
        int c = getchar();
        if (c == '\n' || c == EOF) {
            if (c == EOF && i == 0)
                break;
            if (linenum++ == 0) {
                lmin = lmax = i;
                strcpy(min, input);
                strcpy(max, input);
            } else
            if (lmin > i) {
                lmin = i;
                strcpy(min, input);
            } else
            if (lmax < i) {
                lmax = i;
                strcpy(max, input);
            }
            if (c == EOF)
                break;
            i = 0;
        } else {   
            if (i < LINE_SIZE - 1) {
                input[i] = c;
                input[i + 1] = '\0';
            }
            i++;
        }
    }
    if (linenum == 0) {
        printf("empty file\n");
    } else
    if (linenum == 1) {
        printf("single line (length=%zu): '%s'\n", lmin, min);
    } else {
        printf("min string (length=%zu): '%s'\n", lmin, min);
        printf("max string (length=%zu): '%s'\n", lmax, max);
    }
    return 0;
}

Notes:

  • The above code supports arbitrary line lengths, but only the first 99 characters of the shortest and longest lines are printed.
  • Both the shortest and the longest line lengths may be greater than 100,
  • The code handles empty lines as well as the empty file.
  • The code handles another special case: the line of the input stream may have no trailing newline.
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Alternatively, `char input[100] = { '\0' }`. – melpomene Oct 02 '17 at 06:40
  • @melpomene: that's correct, but if configured for high warning level (`gcc -Wall -Wextra`) the compiler may complain about missing initializers, whereas `char input[100] = ""` will not produce any warnings. – chqrlie Oct 02 '17 at 06:44
  • I'm pretty sure gcc only complains if you get the nesting level wrong. – melpomene Oct 02 '17 at 06:45
  • @melpomene: more precisely, gcc makes a special case of the `{ 0 }` initalizer. it complains for `struct { int a, b; } x = { 1 };`, but not for `struct { int a, b; } x = { 0 };`. – chqrlie Oct 02 '17 at 06:58
  • Small question, how do I only output one value when the user only input one string and then EOF? Everything works fine but I don't want the program to output both max and min when they are the same ? –  Oct 02 '17 at 07:55
  • @CrouchinCorner: you could count the number of lines and make special cases for `linenum == 0` (empty file) and `linenum == 1` (single line). But note that you could still have the same string for shortest and longest if all lines have the same length. Answer updated for example's sake. – chqrlie Oct 02 '17 at 08:05
0

The only ammendment you need to make in your solution is to first check if strlen(min)==0 and if it is, copy input into min. If it isn't, go into your check to see if input's length is less then min.

0

Here is an issue:

int main(void)
{

    char c;
    int i = 0, j = 0, n;
    bool is_first = true;
    char input[100] = { NULL }, max[100] = { NULL }, min[100] = { NULL };

    while( (c = getchar()) != EOF)
    {

        input[i++] = c;
        if(c == '\n') {
            if (is_first)
            {
                strcpy(min, input);
                is_first = false;
            }

           // The rest of your method
        }
    }
}

Explanations:

You declare a is_first at true.
When you get the full string, check if its value is true, then copy the first string into min and compare after with the others strings.

YaatSuka
  • 231
  • 1
  • 9
0

I wouldn't call strlen again and again. Instead I would use an integer type, i.e. size_t, for holding the max and min so far.

Like

#include <limits.h>
#include <stdint.h>

...


size_t maxlen = 0;
size_t minlen = SIZE_MAX;

...

            size_t temp = strlen(input);
            if (temp >= maxlen)
            {
                strcpy(max, input);
                maxlen = temp;
            }

            if (temp <= minlen)
            {
                strcpy(min, input);
                minlen = temp;
            }
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • 1
    Don't use int to store lengh :) Use size_t instead. strlen returns size_t btw http://pubs.opengroup.org/onlinepubs/009695399/functions/strlen.html –  Oct 02 '17 at 05:34
  • Let alone int is signed, size_t is not. –  Oct 02 '17 at 05:38
  • It gives me an error saying "SIZE_MAX undeclared" ? Is that just a number or keyword? –  Oct 02 '17 at 07:01
0

It's not entirely clear what you are attempting. However, I understand you want to read a number of lines, and from those lines, determine the longest (stored in max) and the shortest (stored in min) and then be able to output the longest and shortest along with the number of characters.

That can be done without much difficulty, but I would suggest more meaningful variable names for the length for the max (say lmax) and for the min (say lmin). Those are much more descriptive that i, j.

Since you are reading with getchar(), there is one more (not so uncommon) corner-case you must consider and handle. That being when the final line in the file you are reading from does not end with a '\n' (called a non-POSIX line end). You can do that by simply checking if characters remain unprocessed after the loop ends.

Putting those pieces together, you could do something like the following:

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

#define MAXC 100

int main (void)
{
    int c;
    int i = 0, n, lmin = MAXC, lmax = 0;
    char input[MAXC] = "", 
        max[MAXC] = "", 
        min[MAXC] = "";

    while ((c = getchar()) != EOF)    /* read each char until EOF */
    {
        input[i++] = c;               /* store in input, advance counter */

        /* (i == maxc - 1) or (c == \n), test max/min */
        if (i + 1 == MAXC || c == '\n') {
            if (c == '\n')
                input[--i] = 0;       /* nul-terminate */
            else
                input[i] = 0;
            if ((n = strlen (input)) > lmax) {  /* it's a new max */
                lmax = n;             /* save length */
                strcpy (max, input);  /* copy input to new max */
            }
            if (n < lmin) {           /* its a new min, handle same way */
                lmin = n;
                strcpy (min, input);
            }
            input[0] = 0;  /* set input to empty-string */
            i = 0;         /* zero character index */
        }
    }
    if (i > 0) {        /* handle last line w/o POSIX line end */
        input[i] = 0;   /* nul-terminate, preform same tests */
        if ((n = strlen (input)) > lmax) {
            lmax = n;
            strcpy (max, input);
        }
        if (n < lmin) {
            lmin = n;
            strcpy (min, input);
        }
    }

    printf ("longest  (%2d-chars) : '%s'\n"
            "shortest (%2d-chars) : '%s'\n",
            lmax, max, lmin, min);

    return 0;
}

Example Input

$ cat dat/sentences.txt
my dog has fleas
poor dog
my cat has none
lucky cat
which animal should I keep?

Example Use/Output

$ ./bin/longshortstr <dat/sentences.txt
longest  (27-chars) : 'which animal should I keep?'
shortest ( 8-chars) : 'poor dog'

If this wasn't the output you are looking for, let me know and I'm happy to help further.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85