Elixir 为什么文件读取会因STDIO或file.open的不同而不同? 问题:

Elixir 为什么文件读取会因STDIO或file.open的不同而不同? 问题:,elixir,Elixir,根据我是从:stdio还是打开文件,读取文件的行为似乎有所不同。为什么?我希望能够从STDIN或打开文件(file.open)读取二进制文件,并使用相同的代码提取字节 简言之: 为什么我在下面概述的行为在stdio和stdio之间有所不同 档案 我怎样才能实现我的目标 测试用例 作为一个简单的测试用例,我有一个包含三个字节的二进制文件: 06 8C 7D 我期望的结果是,从任何一个源读取此文件都会产生以下形式的二进制文件: <<6, 140, 125>> 然而

根据我是从
:stdio
还是打开文件,读取文件的行为似乎有所不同。为什么?我希望能够从STDIN或打开文件(
file.open
)读取二进制文件,并使用相同的代码提取字节

简言之:

  • 为什么我在下面概述的行为在stdio和stdio之间有所不同 档案
  • 我怎样才能实现我的目标
测试用例 作为一个简单的测试用例,我有一个包含三个字节的二进制文件:

06 8C 7D
我期望的结果是,从任何一个源读取此文件都会产生以下形式的二进制文件:

<<6, 140, 125>>

然而,根据我是从STDIO读取还是打开文件,情况似乎有所不同

下面是一系列的测试用例来演示这种行为

例1 stdio的IO.binread产生此错误

IO.inspect IO.binread(:stdio, 3)
$ elixir repro.exs < repro.bin
{:error, :collect_chars}
IO.inspect IO.binread(:stdio,3)
$elixir repo.exs
例2 stdio的IO.read产生期望的结果

IO.inspect IO.read(:stdio, 3)
$ elixir repro.exs < repro.bin
<<6, 140, 125>>
{:ok, file} = File.open("repro.bin")
IO.inspect IO.binread(file, 3)
$ elixir repro.exs
<<6, 140, 125>>
IO.inspect IO.read(:stdio,3)
$elixir repo.exs
例3 文件的IO.binread产生所需的结果

IO.inspect IO.read(:stdio, 3)
$ elixir repro.exs < repro.bin
<<6, 140, 125>>
{:ok, file} = File.open("repro.bin")
IO.inspect IO.binread(file, 3)
$ elixir repro.exs
<<6, 140, 125>>
{:ok,file}=file.open(“repo.bin”)
IO.inspect IO.binread(文件,3)
$elixir repo.exs
例4 文件的IO.read添加了一个额外的字节(194),我不明白-我最好的猜测是这与utf8有关

{:ok, file} = File.open("repro.bin")
IO.inspect IO.read(file, 3)
$ elixir repro.exs
<<6, 194, 140, 125>>
{:ok,file}=file.open(“repo.bin”)
IO.inspect IO.read(文件,3)
$elixir repo.exs
我想要的是: 一种接受文件或stdio并处理 两种设备都是相同的。现在,似乎我不能。尽管我在谷歌上搜索得很好,但我发现自己被卡住了


有什么见解吗?

何塞·瓦利姆(JoséValim)对长生不老药谷歌集团的回答:

您的问题的答案是源代码的编码方式。默认情况下,STDIO是unicode格式的,这意味着它不适合binread。这记录在binread函数中,目前是一个Erlang错误/限制。要查找编码,请使用getopts:

iex>:io.getopts:standard\u io
[expand\u-fun:&IEx.Autocomplete.expand/1,echo:true,binary:true,
编码::unicode]

另一方面,文件是拉丁语的,这意味着read将尝试转换,binread将返回原始字节。您可以尝试使用:io.setopts并查看是否得到所需的结果:

iex>io.setopts:标准io,编码::拉丁1

我知道情况并不理想。如果binread始终可以读取字节,而不考虑文件的编码,那就太好了。我在这里写了一份报告:

总而言之:

  • read将始终尝试对设备编码进行转换
  • binread应该总是返回原始二进制文件,但在unicode(这是IO设备的默认设置)方面存在缺陷
我看到的额外字节(194)的奇怪“注入”似乎是elixir/erlang试图将bin解释为utf8

根据他的建议,直接设置stdio的编码似乎可以做到:

test\u read=fn(设备)->
IO.binread(设备,3)
结束
#将stdio的编码设置为latin1
:io.setopts(:标准io,编码::拉丁1)
#根据stdio测试读数
检查测试读数。(:stdio)
#获取一个文件描述符
{:好的,fd}=File.open(“repo.bin”)
#对文件测试相同的读取
IO.检查测试读数(fd)
输出:

$ elixir repro.exs < repro.bin
<<6, 140, 125>>
<<6, 140, 125>>
$elixir repo.exs