文本文件内容的文件大小和字符串字节长度之间的区别?

Difference between file size and string bytelength of text file content?

我正在尝试用 Tcl 编写一个非常小的特定于应用程序的本地服务器,但不了解确定 Content-length 的正确方法。我读到它是字节或八位字节的十进制数。

在下面的代码中,[file size "index.html"] returns 正确的长度使得浏览器 read/loads 所有的内容;但是 [string bytelength $html] 太小了,浏览器没有读到最后。

为什么会这样,有没有更好的方法?谢谢。

if { $op eq "GET" } {
  if { $arg eq "/" } {
    set fp [open "index.html" r]
    set html [read $fp]
    set resp "HTTP/1.1 200 OK\n"
    append resp "Connection: Keep-Alive\n"
    append resp "Content-Type: text/html; charset: utf-8\n"
    append resp "Content-length: [file size "index.html"]\n\n"
    #append resp "Content-length: [string bytelength $html]\n\n"
    append resp $html
    puts stdout $resp
    puts $so $resp
    close $fp
    unset html resp
  }
  # Remainder of if $arg
}

file size 的结果是文件在磁盘上占用的字节数,正是 OS 报告的数字。 (这也是您打开文件并 seek 编辑到结尾时所处的偏移量。)

如果您以 二进制模式读取文件, 您读取的 string length 将与 file size 相同。当以(默认)文本模式读取文件时,它是不同的,因为它取决于读取文件的编码;像 UTF-8 这样的编码可以使用多个字节来描述一个字符,string length 报告一个字符串中的字符数。

string bytelength 命令报告数据使用 Tcl 内部编码(与 UTF-8 非常相似,但不完全相同;有特定的非规范化)编码时所使用的字节数。该编码通常不会暴露给外界,只有 C 扩展才真正感兴趣。当然,那些 C 扩展可以很容易地为自己获取字符串的长度:它是由 Tcl_GetStringFromObj() 生成的(作为 OUT 参数,因为字符串本身是 return 值)所以 string bytelength不是很有用。事实上,我只发现 一个 (1) 合法使用它, 并且与该扩展的集成工作做得更好摆脱它。

string bytelength 报告的值 不是 值当前使用的存储量,而只是(与静态差异密切相关)标准“字符串”解释使用的存储量。如果该值也有任何其他(“内部”)表示,这是常见的(数字、二进制数据、真正的 unicode 数据、列表、字典、命令名称、通道处理程序、可执行代码,所有这些都可能有额外的表示数据)那就是算了。

在您的情况下,您想以 binary 模式打开文件并使用它。还要这样做:

set filename "index.html"
set fp [open $filename rb];   # NB: rb — b is for BINARY; this is important
set size [file size $filename]

# HTTP spec says headers are ISO 8859-1 and CRLF-separated
fconfigure $so -encoding iso8859-1 -translation crlf
set headers ""
append headers "HTTP/1.1 200 OK\n"
append headers "Connection: Keep-Alive\n"
# Detecting the content type of a file is its own chunk of complexity
append headers "Content-Type: text/html; charset: utf-8\n"
append headers "Content-length: $size\n"
puts stdout $headers
puts $so $headers

# Ship the data in binary mode; fcopy is VERY efficient
fconfigure $so -translation binary
fcopy $fp $so -size $size
close $fp

由于使用了混合编码,将 HTTP 消息写入控制台有点混乱;写文件的主体通常不是一个好主意。但是为了调试你会这样做:

set data [read $fp]
puts stdout $data
# Additional -nonewline to not add a line terminator
puts -nonewline $so $data

然而,fcopy 命令(在较新的 Tcl 中也称为 chan copy 作为命令系统化工作的一部分)在将二进制数据从一个地方移动到另一个地方时效率更高。我们可以显着提高效率的唯一方法是将副本移动到 OS 内核中。


tl;dr: 你不想使用 string bytelength。它的作用几乎没有用。