简体   繁体   中英

Assigning a string to Perl substr?

I am looking at Perl script written by someone else, and I found this:

$num2 = '000000';
substr($num2, length($num2)-length($num), length($num)) = $num;
my $id_string = $text."_".$num2

Forgive me ignorance, but for an untrained Perl programmer the second line looks as if the author is assigning the string $num to the result of the function substr . What does this line exactly do?

Exactly what you think it would do:

$ perldoc -f substr

You can use the substr() function as an lvalue, in which case EXPR must itself be an lvalue. If you assign something shorter than LENGTH, the string will shrink, and if you assign something longer than LENGTH, the string will grow to accommodate it. To keep the string the same length, you may need to pad or chop your value using "sprintf".

In Perl, (unlike say, Python, where strings, tuples are not modifiable in-place), strings can be modified in situ. That is what substr is doing here, it is modifying only a part of the string. Instead of this syntax, you can use the more cryptic syntax:

substr($num2, length($num2)-length($num), length($num),$num);

which accomplishes the same thing. You can further stretch it. Imagine you want to replace all instances of foo by bar in a string, but only within the first 50 characters. Perl will let you do it in a one-liner:

substr($target,0,50) =~ s/foo/bar/g;

Great, isn't it?

"Exactly", you ask?

Normally, substr returns a boring string (PV with POK).

$ perl -MDevel::Peek -e'$_="abcd"; Dump("".substr($_, 1, 2));'
SV = PV(0x99f2828) at 0x9a0de38
  REFCNT = 1
  FLAGS = (PADTMP,POK,pPOK)
  PV = 0x9a12510 "bc"\0
  CUR = 2
  LEN = 12

However, when substr is evaluated where an lvalue (assignable value) is expected, it returns a magical scalar (PVLV with GMG (get magic) and SMG (set magic)).

$ perl -MDevel::Peek -e'$_="abcd"; Dump(substr($_, 1, 2));'
SV = PVLV(0x8941b90) at 0x891f7d0
  REFCNT = 1
  FLAGS = (TEMP,GMG,SMG)
  IV = 0
  NV = 0
  PV = 0
  MAGIC = 0x8944900
    MG_VIRTUAL = &PL_vtbl_substr
    MG_TYPE = PERL_MAGIC_substr(x)
  TYPE = x
  TARGOFF = 1
  TARGLEN = 2
  TARG = 0x8948c18
  FLAGS = 0
  SV = PV(0x891d798) at 0x8948c18
    REFCNT = 2
    FLAGS = (POK,pPOK)
    PV = 0x89340e0 "abcd"\0
    CUR = 4
    LEN = 12

This magical scalar holds the parameters passed to susbtr (TARG, TARGOFF and TARGLEN). You can see the scalar pointed by TARG (the original scalar passed to substr ) repeated at the end (the SV at 0x8948c18 you see at the bottom).

Any read of this magical scalar results in an associated function to be called instead. Similarly, a write calls a different associated function. These functions cause the selected part of the string passed to substr to be read or modified.

perl -E'
   $_ = "abcde";
   my $ref = \substr($_, 1, 3);  # $$ref is magical
   say $$ref;                    # bcd
   $$ref = '123';
   say $_;                       # a123e
'

Looks to me like it's overwriting the last length($num) characters of $num2 with the contents of $num in order to get a '0' filled number.

I imagine most folks would accomplish this same task w/ sprintf()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM