【问题标题】:Template Toolkit's somevar.substr() and UTF-8模板工具包的 somevar.substr() 和 UTF-8
【发布时间】:2011-04-12 08:31:41
【问题描述】:

我们在 Catalyst 应用程序中使用模板工具包。我们将 TT 配置为使用 UTF-8,之前没有任何问题。

现在我调用字符串 var 的 substr() 方法。不幸的是,它确实在 n 个字节而不是 n 个字符之后分割字符串。如果第 n 和第 (n+1) 字节构建了一个 unicode char,它将被拆分,并且只有第一个字节是 substr() 结果的一部分。

如何修复或解决该行为?

[% string = "fööbär";

string.length; # prints 9

string.substr(0, 5); # prints "föö" (1 ascii + 2x 2 byte unicode)

string.substr(0, 4): # prints "fö?" (1 ascii, 1x 2 byte unicode, 1 unknown char)
%]

到目前为止,我们对 Unicode 字符没有任何问题,既不是来自数据库,也不是来自模板中的文本。

编辑:这是我在 Catalyst 应用程序中配置 Catalyst::View::TT 模块的方式:

__PACKAGE__->config(
#   DEBUG => DEBUG_ALL,
    DEFAULT_ENCODING => 'utf-8',
    INCLUDE_PATH => My::App->path_to( 'root', 'templates' ),
    TEMPLATE_EXTENSION => '.tt',
    WRAPPER => "wrapper/default.tt",
    render_die => 1,
);

【问题讨论】:

  • 我的建议(在我的答案的更新中添加)在您的源代码中使用 use utf8 指令是否可以解决您的问题?如果没有,您能否提供更多细节以帮助人们提供有用的答案?
  • “2x 2 字节 Unicode”是什么意思? Unicode“总是”2字节(至少)。否则你根本就没有 Unicode 文本——你有一个“编码”的 Unicode 文本流。在这种情况下,您需要检查特定的编码方法。
  • @Stephen:如标题所示,这是关于 UTF-8 的。我认为 UTF-8 是几种 Unicode 编码的子集。无论确切的终端是什么,我都使用 UTF-8。
  • @halo,对不起。我没注意到。
  • @Stephen:不,Unicode 肯定不是“至少总是 2 字节”。 Unicode 是零字节:它与编码形式无关。它是一组非负整数。

标签: perl encoding utf-8 substr template-toolkit


【解决方案1】:

我使用 Perl 1.12.2 对 MSWin32 模板模块进行了快速测试。 它可以正确处理所有这些 substr 操作。

这是我的测试代码:

use Template;

# some useful options (see below for full list)
my $config = {
#    DEFAULT_ENCODING => 'utf-8',
    INCLUDE_PATH => 'd:/devel/perl',  # or list ref
    INTERPOLATE  => 1,               # expand "$var" in plain text
    EVAL_PERL    => 1,               # evaluate Perl code blocks
};

# create Template object
my $template = Template->new($config);

# define template variables for replacement
my $vars = {
    var1  => "abcdef"
};

# specify input filename, or file handle, text reference, etc.
my $input = 'ttmyfile.txt';

# process input template, substituting variables
print $template->process($input, $vars);

ttmyfile.txt

Var = [% var1 %]

[% string = "fööbär" -%]
[% string.length %]   # prints 6
[% string.substr(0, 5) %]  # prints "fööbä"
[% string.substr(0, 4) %]  # prints "fööb" 

输出:

Var = abcdef

6     # prints 6
fööbä  # prints "fööbä"
fööb  # prints "fööb" 
1

一切正常,即使没有use utf8DEFAULT_ENCODING。这里的关键点:

  1. 确保您的模板.tt 文件使用BOM -- Byte Order Mark 编码为UTF8。这是必须做的任务!因为 Template-Toolkit 是根据 BOM 检测 Unicode 文件编码。

    • 您可以使用 Windows 记事本保存带有 BOM 的文件,只需执行 File --> Save --> 编码:“UTF-8”。
    • 你也可以用vim制作,输入set fenc=utf8set bomb,然后保存文件,文件将以BOM开头。
  2. NCODING 参数Template->new({NCODING => 'utf-8'}); 设置为“utf-8”将强制Template 将模板文件加载为“utf-8”。

  3. 建议在您的脚本中包含use utf8,这将确保您的所有内联字符串都正确编码为utf8。

因为Catalyst::View::TT 依赖于模板,我相信它应该也能正常工作!祝你好运~~~

【讨论】:

  • 谢谢你的例子。不幸的是,它对我不起作用:(我将文件保存为没有 BOM 的 UTF8 并且 fööb 行读取 fö?length 打印 9 而不是 6。之后我保存了带有 BOM 和长度的两个文件 6 但是所有变音符号都变成了问号。我觉得这可能是我的系统有问题。我会确定这一点。操作系统是 Fedora 14 KDE Spin。我在其他任何地方都没有编码问题。
  • 好,你做对了!问号是由您的终端引起的,而不是由编码本身引起的。您可以简单地将输出重定向到一个文件,并使用您的编辑器检查该文件。这将是你想要的正确的东西!
  • 不,UTF-8 不应该有 BOM。
  • @tchrist 请参考unicode.org/faq/utf_bom.html#BOM 我认为这真的取决于协议。
  • 我发现Template->new({NCODING => 'utf-8'}) 使TT 读取 UTF8 正确。因此它可以正确检测字符串长度。使用binmode STDOUT, ":encoding(utf8)";,我可以强制 output to TTY 为 UTF8,一切都很好。 use uft8 完全没有区别。任何提示如何实现输出到 Catalyst?这是带有注释示例的 pastebin pastebin.com/X6xs1rtd
【解决方案2】:

关于 UTF-8 的 Wikipedia 文章提供了一个表格,显示了非 ASCII 字符的encoded。该表说明了 UTF-8 的以下简单规则:

  • 如果一个字节的最高位为0,则该字节表示一个ASCII字符。

  • 如果一个字节的最高两位是11,那么这是一个多字节字符的开始,从最高位开始的连续1位的个数表示该字节的总字节数多字节字符。因此,位表示为 110xxxxxx 的字节是 2 字节字符的开头,1110xxxx 是 3 字节字符的开头,而 11110xxx 是 4 字节字符的开头。 (您可以忽略假设的 5 字节和 6 字节字符,因为 Unicode 仅限于 21 位字符集而不是 32 位字符集。)

  • 如果一个字节的两个最高位是 10,则该字节是多字节字符的一部分(但 不是该字符的第一个字节)。

这些信息应该足以让您编写自己的实用程序函数,例如 string.lengthstring.substring(),但使用字符而不是字节。

更新:问题没有具体说明所使用的编程语言,我不知道“模板工具包”暗示使用 Perl。意识到这一点后,我进行了 Google 搜索,发现您的问题很可能是由于需要在源代码中添加 use utf8 指令所致。你可以找到关于这个here的讨论。

【讨论】:

  • 谢谢你。正如您在 perldoc 中看到的那样,您链接的 use uft8 仅用于告诉 Perl 源代码本身 保存在 utf8 中。正如编辑所示,我已经将Catalyst::View:TT 配置为使用 UTF-8。但是,我尝试将 use uft8 添加到 Catalyst::View:TT 模块,但没有帮助。
【解决方案3】:

答案很简单(在 Perl 中),幸运的是:

use Encode qw{encode decode};

它的工作方式是将 Unicode 字符串解码为 Perl 字符串,然后您可以按照您期望的方式使用 substr() 和 length(),然后再次对它们进行编码以输出。

使用该标题:

# $unicodeString = 'fööbär';
my $perlString = decode('UTF-8', $unicodeString);
printf "%d\n", length($perlString);  # should be 6
printf "%s\n", substr($perlString, 0, 3);  # should be 'föö'
# whatever other processing you want here with $perlString . . .
# Then, you want to reencode that back to a proper UTF-8 string:
my $unicodeString = encode('UTF-8', $perlString);

会有帮助吗?

【讨论】:

  • 我强烈建议您根本不要处理编码字符串。确保它们被尽快解码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-18
  • 2011-12-26
  • 2011-05-18
  • 2012-07-29
  • 2010-12-22
  • 2019-01-03
  • 2012-07-03
相关资源
最近更新 更多