http://www.perlmonks.org?node_id=4688

vroom has asked for the wisdom of the Perl Monks concerning the following question: (strings)

How do I get the Nth Character of a String?

Originally posted as a Categorized Question.

  • Comment on How do I get the Nth Character of a String?

Replies are listed 'Best First'.
Re: How do I get the Nth Character of a String?
by gridlock (Novice) on Feb 21, 2004 at 04:39 UTC
    The substr function:
    Remember the strings are Zero-Based - i.e. - the first character is character '0', the second character is character '1' and so on....

    By that logic, the n-th character is character '(n-1)'

    So you're n-th character can be extracted as
    $nth = substr($string, n-1, 1);
Re: How do I get the Nth Character of a String?
by btrott (Parson) on Mar 04, 2000 at 01:31 UTC
    Some might consider using split and an index. The problem with this solution is that it's really slow, particularly as the string gets longer.

    Here's some benchmarking code that'll show you:

    #!/usr/local/bin/perl -w use strict; use Benchmark; use vars qw/$str/; $str = "123456789asgdjlaskjglkajblnlbnlaqjteoijqotijwojgl;akjglkj"; timethese(shift || 10000, { 'unpack' => sub { my $char = getn_unpack($str, 30) }, 'substr' => sub { my $char = getn_substr($str, 30) }, 'split' => sub { my $char = getn_split($str, 30) } }); sub getn_unpack { return unpack "x" . ($_[1]-1) . "a", $_[0]; } sub getn_substr { return substr $_[0], $_[1]-1, 1; } sub getn_split { return +(split //, $_[0])[$_[1]-1]; }
    And here are the benchmark results:
    Benchmark: timing 100000 iterations of split, substr, unpack... split: 20 secs (19.39 usr 0.00 sys = 19.39 cpu) substr: 0 secs ( 1.78 usr 0.00 sys = 1.78 cpu) unpack: 2 secs ( 2.89 usr 0.00 sys = 2.89 cpu)
    The substr method is, obviously, the fastest, because that's what it's designed to do, basically.

    unpack is also quite fast.
    split has to split the entire string, construct an array of length($str) elements, then take an index.

    So, the best way is to use substr.

      chr vec $_[0],$_[1]-1,8; #is slightly faster
      (but you should also think about why you want to do this, perhaps you're approaching the problem in a Cish way rather than a Perlish way)
Re: How do I get the Nth character of a string?
by vroom (Pope) on Jan 21, 2000 at 00:56 UTC
    Use substr with a length of one. The character numbering is zero based so the first character in the string is the 0th element.
    $str = "123456789"; $first = substr($str, 0, 1); #returns the first character $third = substr($str, 2, 1); #returns the third character
Re: How do I get the Nth Character of a String?
by PetaMem (Priest) on Sep 14, 2009 at 13:42 UTC

    split is slow. However, if you want just the first character of the string (I often do), then you can significantly speed up split by giving it the value 2 as LIMIT argument.

    Benchmarked code from above (obviously on a faster machine) with 1 million iterations:

    split: 17 wallclock secs (16.69 usr + 0.02 sys = 16.71 CPU) @ 59 +844.40/s (n=1000000) substr: 0 wallclock secs ( 0.74 usr + 0.00 sys = 0.74 CPU) @ 13 +51351.35/s (n=1000000) unpack: 1 wallclock secs ( 1.02 usr + 0.00 sys = 1.02 CPU) @ 98 +0392.16/s (n=1000000)
    Benchmarking a very similar code, but instead looking for the 1st char only and giving split a ,2 limit:
    split: 2 wallclock secs ( 1.16 usr + 0.00 sys = 1.16 CPU) @ 86 +2068.97/s (n=1000000) substr: 1 wallclock secs ( 0.70 usr + 0.00 sys = 0.70 CPU) @ 14 +28571.43/s (n=1000000) unpack: 0 wallclock secs ( 0.96 usr + 0.00 sys = 0.96 CPU) @ 10 +41666.67/s (n=1000000)
    #!/usr/bin/perl -w use strict; use Benchmark; use vars qw/$str/; $str = "123456789asgdjlaskjglkajblnlbnlaqjteoijqotijwojgl;akjglkj"; timethese(shift || 1000000, { 'unpack' => sub { my $char = getn_unpack($str, 1) }, 'substr' => sub { my $char = getn_substr($str, 1) }, 'split' => sub { my $char = getn_split($str, 1) } }); sub getn_unpack { return unpack "x" . ($_[1]-1) . "a", $_[0]; } sub getn_substr { return substr $_[0], $_[1]-1, 1; } sub getn_split { return +(split //, $_[0],2)[$_[1]-1]; }

    Still slower than the other solutions, but nevertheless a speedup of over 10 to an unrestricted split. See if using the LIMIT argument in split would help your code.