Is there a quick way to get the last element that was put in an array?

Viewed 322

I use an fgets to read from stdin a line and save it in a char array, I would like to get the last letter of the line i wrote , which should be in the array before \nand \0.

For example if i have a char line[10] and write on the terminal 1stLine, is there a fast way to get the letter e rather than just cycling to it?

I saw this post How do I print the last element of an array in c but I think it doesn't work for me, even if I just create the array without filling it with fgets , sizeof line is already 10 because the array already has something in it

I know it's not java and I can't just .giveMeLastItem(), but I wonder if there is a smarter way than to cycle until the char before the \n to get the last letter I wrote

code is something like

char command[6];
fgets(command,6,stdin);
7 Answers

If you know the sentinel value, ex: \0 (or \n ,or any value for that matter), and you want the value of the element immediately preceding to that, you can

  • use strchr() to find out the position of the sentinel and
  • get the address of retPtr-1 and dereference to get the value you want.

Once you have only the array, there is no other way to do this. You could use strlen(line) and then get the last characters position based on this index, but this basically does exactly the same (loop over the array).

char lastChar = line[strlen(line)-1];

This has time-complexity of O(n), where n is the input length.

You can change the input method to a char by char input and count the length or store the last input. Every O(1) method like this uses O(n) time before (like n times O(1) for every character you read). But unless you have to really speed optimize (and you don't, when you work with user input) should just loop over the array by using a function like strlen(line) (and store the result, when you use it multiple times).

EDIT: The strchr() function Sourav Ghosh mentioned, does exactly the same, but you can/must specify the termination character.

There are many different ways to inspect the line read by fgets():

  • first you should check the return value of fgets(): a return value of NULL means either the end of file was reached or some sort of error occurred and the contents of the target array is undefined. It is also advisable to use a longer array.

      char command[80];
      if (fgets(command, sizeof command, stdin) == NULL) {
          // end of file or read error
          return -1;
      }
    
  • you can count the number of characters with len = strlen(command) and if this length os not zero(*), command[len - 1] is the last character read from the file, which should be a '\n' if the line has less than 5 bytes. Stripping the newline requires a test:

      size_t len = strlen(command);
      if (len > 0 && command[len - 1] == '\n')
          command[--len] = '\0';
    
  • you can use strchr() to locate the newline, if present with char *p strchr(command, '\n'); If a newline is present, you can strip it this way:

      char *p = strchar(command, '\n');
      if (p != NULL)
          *p = '\0';
    
  • you can also count the number of characters no in the set "\n" with pos = strcspn(command, "\n"). pos will point to the newline or to the null terminator. Hence you can strip the trailing newline with:

      command[strcspn(command, "\n")] = '\0';  // strip the newline if any
    
  • you can also write a simple loop:

      char *p = command;
      while (*p && *p != '\n')
          p++;
      *p = '\n'; // strip the newline if any
    

(*) strlen(command) can return 0 if the file contains an embedded null character at the beginning of a line. The null byte is treated like an ordinary character by fgets(), which continues reading bytes into the array until either size - 1 bytes have been read or a newline has been read.

A straightforward approach can look the following way

char last_letter = command[ strcspn( command, "\n" ) - 1 ];

provided that the string is not empty or contains just the new line character '\n'.

Here is a demonstrative progarm.

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

int main(void) 
{
    enum { N = 10 };
    char command[N];
    
    while ( fgets( command, N, stdin ) && command[0] != '\n' )
    {
        char last_letter = command[ strcspn( command, "\n" ) - 1 ];
        printf( "%c ", last_letter );
    }
    
    putchar( '\n' );
    
    return 0;
}

If to enter the following sequence of strings

Is
there
a
quick
way
to
get
the
last
element
that
was
put
in
an
array?

then the output will be

s e a k y o t e t t t s t n n ? 

The fastest way is to keep an array of references like this:

long ref[]

and ref[x] to contain the file offset of the last character of the xth line. Having this reference saved at the beginning of the file you will do something like:

fseek(n*sizeof(long))
long ref = read_long()
fseek(ref)
read_char()

I think this is the fastest way to read the last character at the end of the nth line.

Beside the other good examples.

Another way is using fscanf()/scanf() and the %n format specifier to write to an argument the amount of read characters so far after you have input the string.

Then you subtract this number by one and use it as an index to command:

char command[6];
int n = 0;

if (fscanf(stdin, "%5[^\n]" "%n", command, &n) != 1)
{
    fputs("Error at input!", stderr);
    // error routine.
}

getchar();

if (n != 0)
{
    char last_letter = command[n-1];
}

#include <stdio.h>

int main (void)
{
    char command[6];
    int n = 0;

    if (fscanf(stdin, "%5[^\n]" "%n", command, &n) != 1)
    {
        fputs("Error at input!", stderr);
        // error routine.
    }

    getchar();
    
    if (n != 0)
    {
        char last_letter = command[n-1];
        putchar(last_letter);
    }

    return 0;
}

Execution:

./a.out
hello
o

I did a quick test of the three mentioned methods of reading a line from a stream and measuring its length. I read /usr/share/dict/words 100 times and measured with clock()/1000:

fgets + strlen = 420
getc = 510
fscanf with " 100[^\n]%n" = 940

This makes sense as fgets and strlen just do 2 calls, getc does a call per character, and fscanf may do one call but has a lot of machinery to set up for processing complex formats, so a lot more overhead. Note the added space in the fscanf format to skip the newline left from the previous line.

Related