我正在实现一个向服务器发送消息的网络客户端。消息是字节流,协议要求我事先发送每个流的长度。
如果我给出的消息(通过使用我的模块的代码)是一个字节字符串,那么length $string
足够容易给出长度。但如果它是一串字符,我需要按摩它以获得原始字节。我现在正在做的基本上是这样的:
my $msg = shift; # some message from calling code
my $bytes;
if ( utf8::is_utf8( $msg ) ) {
$bytes = Encode::encode( 'utf-8', $msg );
} else {
$bytes = $msg;
}
my $length = length $bytes;
这是处理此问题的正确方法吗?它似乎工作到目前为止,但我还没有做过任何严肃的测试。这种方法有哪些潜在的缺陷?
由于
答案 0 :(得分:4)
你不应该猜测你的输入是什么。 定义您的代码以接受字节字符串或Unicode字符串,并将其留给调用者将输入转换为正确的格式(或者为调用者提供某种方式来指定他们的字符串类型'重新提供)。
如果您将代码定义为接受字节字符串,那么\xFF
以上的任何字符都是错误的。
如果您将代码定义为接受Unicode字符串,那么您可以将它们转换为Encode::encode_utf8()
的字节(并且无论Perl如何在内部表示它们都应该这样做。)
在任何情况下,调用utf8::is_utf8()
通常都是错误的 - 您的程序不应该关心字符串的内部表示,而只关心它们包含的实际数据(字符序列)。其中一些字符(特别是\x80
到\xFF
范围内的那些字符)是否由一个或两个字节在内部表示无关紧要。
聚苯乙烯。阅读perldoc Encode
可能有助于澄清Perl中字节和字符的问题。
答案 1 :(得分:1)
发件人:
use Encode qw( encode_utf8 );
sub pack_text {
my ($text) = @_;
my $bytes = encode_utf8($text);
die "Text too long" if length($bytes) > 4294967295;
return pack('N/a*', $bytes);
}
接收者:
use Encode qw( decode_utf8 );
sub read_bytes {
my ($fh, $to_read) = @_;
my $buf = '';
while ($to_read > 0) {
my $bytes_read = read($fh, $buf, $to_read, length($buf));
die $! if !defined($bytes_read);
die "Premature EOF" if !$bytes_read;
$to_read -= $bytes_read;
}
return $buf;
}
sub read_uint32 {
my ($fh) = @_;
return unpack('N', read_bytes($fh, 4));
}
sub read_text {
my ($fh) = @_;
return decode_utf8(read_bytes($fh, read_uint32($fh)));
}
答案 2 :(得分:0)
perldoc -f length
曾经说过,回到v5.8,
...您将获得字符数,而不是字节数。 要获得以字节为单位的长度,请使用
"do { use bytes; length(EXPR) }"
, 见bytes
。
length
的现代文档未提及bytes
:
length()
通常会处理 逻辑字符,而不是物理字节。对于多少字节a 编码为UTF-8的字符串将占用,使用"length(Encode::encode_utf8(EXPR))"
(你必须“使用 编码“首先”。请参阅Encode
和perlunicode
。
但我不认为弃用do { use bytes; ... }
解决方案。