perlclib - 标准 C 库函数的内部替换
Perl 移植者应注意的一件事是,perl 在内部并不倾向于使用太多的 C 标准库;例如,你几乎看不到其中使用 ctype.h 函数。这是因为 Perl 倾向于重新实现或抽象标准库函数,以便我们确切地知道它们将如何操作。
这是一张参考卡片,适用于熟悉 C 库并希望以 Perl 方式做事的人员;告诉他们应该使用哪些函数,而不是更常见的 C 函数。
在以下表格中
sv
、av
、hv
等表示各自类型的变量。
您应该使用 Perl 抽象层,而不是 stdio.h 函数。您需要处理 PerlIO*
类型,而不是 FILE*
类型。请不要忘记,使用新的 PerlIO 分层 I/O 抽象时,甚至可能无法使用 FILE*
类型。另请参阅 perlapio
文档,以了解有关以下函数的更多信息
Instead Of: Use:
stdin PerlIO_stdin()
stdout PerlIO_stdout()
stderr PerlIO_stderr()
fopen(fn, mode) PerlIO_open(fn, mode)
freopen(fn, mode, stream) PerlIO_reopen(fn, mode, perlio) (Dep-
recated)
fflush(stream) PerlIO_flush(perlio)
fclose(stream) PerlIO_close(perlio)
Instead Of: Use:
fprintf(stream, fmt, ...) PerlIO_printf(perlio, fmt, ...)
[f]getc(stream) PerlIO_getc(perlio)
[f]putc(stream, n) PerlIO_putc(perlio, n)
ungetc(n, stream) PerlIO_ungetc(perlio, n)
请注意,fread
和 fwrite
的 PerlIO 等效项与其 C 库对应项略有不同
fread(p, size, n, stream) PerlIO_read(perlio, buf, numbytes)
fwrite(p, size, n, stream) PerlIO_write(perlio, buf, numbytes)
fputs(s, stream) PerlIO_puts(perlio, s)
没有等效的 fgets
;应该使用 sv_gets
fgets(s, n, stream) sv_gets(sv, perlio, append)
Instead Of: Use:
feof(stream) PerlIO_eof(perlio)
fseek(stream, n, whence) PerlIO_seek(perlio, n, whence)
rewind(stream) PerlIO_rewind(perlio)
fgetpos(stream, p) PerlIO_getpos(perlio, sv)
fsetpos(stream, p) PerlIO_setpos(perlio, sv)
ferror(stream) PerlIO_error(perlio)
clearerr(stream) PerlIO_clearerr(perlio)
Instead Of: Use:
t* p = malloc(n) Newx(p, n, t)
t* p = calloc(n, s) Newxz(p, n, t)
p = realloc(p, n) Renew(p, n, t)
memcpy(dst, src, n) Copy(src, dst, n, t)
memmove(dst, src, n) Move(src, dst, n, t)
memcpy(dst, src, sizeof(t)) StructCopy(src, dst, t)
memset(dst, 0, n * sizeof(t)) Zero(dst, n, t)
memzero(dst, 0) Zero(dst, n, char)
free(p) Safefree(p)
strdup(p) savepv(p)
strndup(p, n) savepvn(p, n) (Hey, strndup doesn't
exist!)
strstr(big, little) instr(big, little)
strcmp(s1, s2) strLE(s1, s2) / strEQ(s1, s2)
/ strGT(s1,s2)
strncmp(s1, s2, n) strnNE(s1, s2, n) / strnEQ(s1, s2, n)
memcmp(p1, p2, n) memNE(p1, p2, n)
!memcmp(p1, p2, n) memEQ(p1, p2, n)
请注意,Copy
和 Move
的参数顺序与 memcpy
和 memmove
中使用的顺序不同。
不过,大多数情况下,您希望在内部处理 SV,而不是原始 char *
字符串
strlen(s) sv_len(sv)
strcpy(dt, src) sv_setpv(sv, s)
strncpy(dt, src, n) sv_setpvn(sv, s, n)
strcat(dt, src) sv_catpv(sv, s)
strncat(dt, src) sv_catpvn(sv, s)
sprintf(s, fmt, ...) sv_setpvf(sv, fmt, ...)
还要注意 sv_catpvf
和 sv_vcatpvfn
的存在,它们将连接与格式化相结合。
有时,您应该考虑“破坏”数据,而不是使用 Newxz() 将分配的堆清零。这意味着向其中写入一个位模式,该模式应作为指针(和浮点数)是非法的,并且希望作为整数时也足够令人惊讶,以便任何尝试在未经深思熟虑的情况下使用该数据的代码都会尽早中断。可以使用 Poison() 宏进行破坏,其参数与 Zero() 类似
PoisonWith(dst, n, t, b) scribble memory with byte b
PoisonNew(dst, n, t) equal to PoisonWith(dst, n, t, 0xAB)
PoisonFree(dst, n, t) equal to PoisonWith(dst, n, t, 0xEF)
Poison(dst, n, t) equal to PoisonFree(dst, n, t)
Perl 实现了几种类型的字符类测试。这里描述的只是那些直接对应于对 8 位字符进行操作的 C 库函数的函数,但还有对应于宽字符和 UTF-8 编码字符串的等效函数。所有这些都在 "perlapi 中的“字符分类” 和 "perlapi 中的“字符大小写转换” 中进行了更全面的描述。
下表中列出的 C 库例程会根据当前区域设置返回相应的值。使用最后一列中的条目来实现该功能。其他两列始终假定为 POSIX(或 C)区域设置。ASCII 列中的条目仅对 ASCII 输入有意义,对于其他任何输入,都会返回 FALSE。仅当您知道这就是您想要的结果时,才使用这些条目。Latin1 列中的条目假定非 ASCII 8 位字符与 Unicode 定义的相同,与 ISO-8859-1(通常称为 Latin 1)相同。
Instead Of: Use for ASCII: Use for Latin1: Use for locale:
isalnum(c) isALPHANUMERIC(c) isALPHANUMERIC_L1(c) isALPHANUMERIC_LC(c)
isalpha(c) isALPHA(c) isALPHA_L1(c) isALPHA_LC(u )
isascii(c) isASCII(c) isASCII_LC(c)
isblank(c) isBLANK(c) isBLANK_L1(c) isBLANK_LC(c)
iscntrl(c) isCNTRL(c) isCNTRL_L1(c) isCNTRL_LC(c)
isdigit(c) isDIGIT(c) isDIGIT_L1(c) isDIGIT_LC(c)
isgraph(c) isGRAPH(c) isGRAPH_L1(c) isGRAPH_LC(c)
islower(c) isLOWER(c) isLOWER_L1(c) isLOWER_LC(c)
isprint(c) isPRINT(c) isPRINT_L1(c) isPRINT_LC(c)
ispunct(c) isPUNCT(c) isPUNCT_L1(c) isPUNCT_LC(c)
isspace(c) isSPACE(c) isSPACE_L1(c) isSPACE_LC(c)
isupper(c) isUPPER(c) isUPPER_L1(c) isUPPER_LC(c)
isxdigit(c) isXDIGIT(c) isXDIGIT_L1(c) isXDIGIT_LC(c)
tolower(c) toLOWER(c) toLOWER_L1(c)
toupper(c) toUPPER(c)
要强调您仅对 ASCII 字符进行操作,您可以在 ASCII 列中的每个宏后面附加 _A
:isALPHA_A
、isDIGIT_A
,依此类推。
(即使有与 isASCII
相同的 isASCII_L1
,Latin1 列中也没有 isascii
的条目;后者的名称更清晰。Latin1 列中也没有 toupper
的条目,因为结果可能是非 Latin1。您必须使用 toUPPER_uvchr
,如 "perlapi 中的“字符大小写转换” 中所述。)
Instead Of: Use:
atof(s) Atof(s)
atoi(s) grok_atoUV(s, &uv, &e)
atol(s) grok_atoUV(s, &uv, &e)
strtod(s, &p) Strtod(s, &p)
strtol(s, &p, n) Strtol(s, &p, b)
strtoul(s, &p, n) Strtoul(s, &p, b)
典型用法是在强制转换之前对 uv
执行范围检查
int i; UV uv;
char* end_ptr = input_end;
if (grok_atoUV(input, &uv, &end_ptr)
&& uv <= INT_MAX)
i = (int)uv;
... /* continue parsing from end_ptr */
} else {
... /* parse error: not a decimal integer in range 0 .. MAX_IV */
}
还要注意 numeric.c 中的 grok_bin
、grok_hex
和 grok_oct
函数,用于将表示相应进制数的字符串转换为 NV
。请注意,grok_atoUV() 不处理负输入或前导空格(故意严格)。
请注意,strtol() 和 strtoul() 可能伪装成 Strtol()、Strtoul()、Atol()、Atoul()。也要避免这些。
理论上,如果 Perl 所构建的机器实际上没有 strtol 和 strtoul,则可能未定义 Strtol
和 Strtoul
。但由于这两个函数是 1989 年 ANSI C 规范的一部分,我们怀疑您现在可以在任何地方找到它们。
int rand() double Drand01()
srand(n) { seedDrand01((Rand_seed_t)n);
PL_srand_called = TRUE; }
exit(n) my_exit(n)
system(s) Don't. Look at pp_system or use my_popen.
getenv(s) PerlEnv_getenv(s)
setenv(s, val) my_setenv(s, val)
您甚至不应该想要使用 setjmp.h 函数,但如果您认为需要,请改用 scope.h 中的 JMPENV
堆栈。
对于 signal
/sigaction
,请使用 rsignal(signo, handler)
。