【问题标题】:How can I convert CGI input to UTF-8 without Perl's Encode module?如何在没有 Perl 的编码模块的情况下将 CGI 输入转换为 UTF-8?
【发布时间】:2023-03-23 09:40:01
【问题描述】:

通过这个论坛,我了解到使用以下方法将 CGI 输入(来自 escape()d Ajax 调用或普通 HTML 表单帖子)转换为UTF-8:

read (STDIN, $_, $ENV{CONTENT_LENGTH});
s{%([a-fA-F0-9]{2})}{ pack ('C', hex ($1)) }eg;
utf8::decode $_;

一种更安全的方法(例如不允许假字符通过)是执行以下操作:

use Encode qw (decode);
read (STDIN, $_, $ENV{CONTENT_LENGTH});
s{%([a-fA-F0-9]{2})}{ pack ('C', hex ($1)) }eg;
decode ('UTF-8', $_, Encode::FB_CROAK);

但是,我非常希望避免使用任何模块(包括 XSLoader、Exporter 以及它们附带的任何其他模块)。该功能适用​​于大容量 mod_perl 驱动的网站,我认为没有模块的性能和可维护性都会更好(特别是因为当前代码没有使用任何模块)。

我想一种方法是检查 Encode 模块并去除用于“decode ('UTF-8', $_, Encode::FB_CROAK)”调用的函数和常量。我对 Unicode 和 Perl 模块不够熟悉,无法做到这一点。也许其他人能够做到这一点,或者知道一种类似的、安全的“本地”方式来进行 UTF-8 转换?

更新:

我更喜欢保持非模块化,因为唯一的黑盒就是 Perl 自己的编译器(当然,除非你深入研究模块库)。

有时您会看到大型模块被几行特定的代码所取代。例如,可以使用以下模块来代替 CGI.pm 模块(人们也喜欢它)来解析 AJAX 帖子:

my %Input;
if ($ENV{CONTENT_LENGTH}) {
    read (STDIN, $_, $ENV{CONTENT_LENGTH});
    foreach (split (/&/)) {
        tr/+/ /; s/%([a-fA-F0-9]{2})/pack("C", hex($1))/eg;
        if (m{^(\w+)=\s*(.*?)\s*$}s) { $Input{$1} = $2; }
        else { die ("bad input ($_)"); }
    }
}

以类似的方式,如果可以提取或复制 Encode 的 UTF-8 解码函数,那就太好了。

【问题讨论】:

  • 编码是 Perl 自带的,难道“做对了”不应该胜过其他任何事情吗?模块就是代码。
  • 我在Effective Perl Programming 中介绍了大部分这些内容,我想我之前已经向您提到过。编码是执行此操作的本机方式。 Perl 将大块的功能分成模块,这样你就不必使用你不想要的东西。
  • 模块不是黑匣子。你可以看看他们的来源。大多数人并不喜欢 CGI。他们向不知道自己在做什么的人推荐它,因为它至少是一个起点。例如,您的 CGI 解析器被严重破坏,原因与其他不知道自己在做什么的人破坏事物的原因相同。例如,& 并不总是参数分隔符,参数可以有多个值。你两个都不处理。查看 CGI.pm 以了解它的作用以及您必须处理的内容。
  • 您说:更安全的方法 [...] 是执行以下操作 [...]But this is not what I said. 我特别推荐URI::Escape::XS 模块而不是unpack。 XS 模块的运行速度与内置模块的速度相同,因此您对性能的关注没有任何影响。正如 brian 所说,profile 首先,您会惊讶于您的程序实际花费的时间。

标签: perl unicode utf-8


【解决方案1】:

请勿使用escape() 创建您发布的数据。这与 URL 编码不兼容,它是一种变异的 JavaScript,通常不应该使用。缺陷之一是它将非 ASCII 字符编码为基于 UTF-16 代码单元的非标准 %uNNNN 序列,而不是标准 URL 编码的 UTF-8。您当前的代码将无法处理。

您通常应该改用encodeURIComponent()

如果您必须自己对发布的输入进行 URL 解码,而不是使用表单库(这确实意味着您将无法处理 multipart/form-data),您需要在替换 @ 之前将 + 符号转换为空格987654327@-序列。这种替换在表单提交中是标准的(尽管在 URL 编码数据的其他地方没有)。

如果您真的不想使用库,要确保输入是有效的 UTF-8,请尝试 this regex。它还排除了一些控制字符(您可能需要调整它以排除更多字符)。

【讨论】:

  • 非常感谢您的意见。我知道 escape()、二进制/多部分发布等的缺点,但是您链接到的 RegEx 似乎非常有用。无论我的 UTF-8 解码方法是否有意义,时间都会证明,但您的回答绝对有帮助,非常感谢!
【解决方案2】:

不要预先优化。先以传统方式进行,然后再进行分析和基准测试,以查看您需要优化的地方。人们通常把所有的时间都浪费在其他地方,所以一开始就蒙上眼睛和戴上手铐不会给你带来任何好处。

不要害怕模块。 mod_perl 的要点是尽可能少地加载所有内容,因此启动时间和模块加载时间是微不足道的。

【讨论】:

  • 当您确切知道自己需要什么时,预优化有什么问题?如果不需要(因为你已经去掉了所有不必要的逻辑),为什么还要麻烦进行基准测试?当然,您对 mod_perl 有意见,而且我普遍承认您对 Perl 的了解大约是我的 1000 倍。所以我当然会考虑您的观点,并期待听到其他人的观点。
  • 好吧,我认为您并不确切知道自己需要什么。听起来你不知道自己在做什么。
  • 非常有建设性的评论。如果我知道该怎么做,我会问问题吗?你看,我相信这就是这个网站的目的——而不是作者宣传商业书籍。
  • 这个网站的目的是帮助那些真正需要帮助的人。你似乎不需要真正的帮助。相反,您正在寻找对您预先设想的想法的验证。我对宣传我的书并不感到难过。我对宣传别人的书并不感到难过。 “你读的越多,你就会知道的东西越多。你学的越多,你去的地方就越多。”这就是我们写书的原因。
猜你喜欢
  • 1970-01-01
  • 2010-12-09
  • 2013-11-15
  • 2015-06-10
  • 1970-01-01
  • 2020-02-06
  • 2012-06-12
  • 2015-01-04
  • 2012-06-30
相关资源
最近更新 更多