将StreamReader转换为byte[]

56

我得到了一个StreamReader对象的结果。

我想将结果转换为byte[]

如何将StreamReader转换为byte[]

谢谢

7个回答

73

将读入的数据全部放到MemoryStream中,最后获取字节数组。注意,应该从底层流中读取原始字节。

var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
    var buffer = new byte[512];
    var bytesRead = default(int);
    while ((bytesRead = reader.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
        memstream.Write(buffer, 0, bytesRead);
    bytes = memstream.ToArray();
}

如果您不想管理缓冲区:

var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
    reader.BaseStream.CopyTo(memstream);
    bytes = memstream.ToArray();
}

为什么要用MemoryStream走这么远呢? - Daniel Hilgarth
@Daniel:除非您事先知道流的长度,否则您将不得不将其读入某种可变长度的变量中。我猜MemoryStream是一个选择。@Jeff:default()的使用是什么意思?我认为这并没有使事情更加可读。 - Matti Virkkunen
3
@Matti:这只是我学习的一种编码风格,我尽可能地使用var关键字(对于大多数局部变量)。我不指望每个人都能接受它,但我可以接受。 - Jeff Mercado
1
@Daniel:MemoryStream 可能会更高效,因为它被设计用于高效处理字节数组。例如,您可以使用 Write 仅添加数组的一部分(与 List<T> 上的 AddRange 不同),而且它不通过接口访问字节数组,这可能会略微降低效率。您还可以直接使用 GetBuffer() 获取底层数组,而无需创建其副本。当然,这只在您有大量数据时才重要。 - Matti Virkkunen
@Matti:只是让你知道,如果GetBuffer()没有在构造函数中传入底层字节数组(即MemoryStream所包装的数组),它将仅返回该数组。否则,您必须使用ToArray()。至于var的东西,我处理的很多代码(特别是其他语言)使用隐式类型,对于本地变量,我喜欢使用它,但对于委托/lambda等有例外。这有点难以解释,所以我就这样了。 :) - Jeff Mercado
显示剩余7条评论

46

StreamReader 用于文本读取,而不是纯字节。不要使用 StreamReader,而是直接从底层流中读取


3
你可以使用CopyTo方法:
var ms = new MemoryStream();
yourStreamReader.BaseStream.CopyTo(ms); // blocking call till the end of the stream
ms.GetBuffer().CopyTo(yourArray, ms.Length);

或者

var ms = new MemoryStream();
var ct = yourStreamReader.BaseStream.CopyToAsync(ms);
await ct;
ms.GetBuffer().CopyTo(yourArray, ms.Length);

1
正如Matti Virkkunen所指出的那样,您不一定需要MemoryStream。当流不太长时,您可以直接写入数组中。分配更少的内存是优势。
using (var stream = File.OpenRead("your_file.ext"))
{
    var length = stream.Length;
    if (length <= int.MaxValue)
    {
        var result = new byte[length];
        var bytesRead = stream.Read(result, 0, (int)length);
        if (bytesRead == length) return result;
    }
    //fallback
    using (var memoryStream = new MemoryStream())
    {
        stream.CopyTo(memoryStream);
        return memoryStream.ToArray();
    }
}

1
        BlobClient blob = container.GetBlobClient(path);
        //blob.DownloadTo(@"temp\"+ file);
        var response = blob.DownloadContent();
        var stream = response.Value.Content.ToStream();
        using (var sr = new StreamReader(stream))
        {
            using (MemoryStream ms = new MemoryStream())
            {
                sr.BaseStream.CopyTo(ms);
                return Convert.ToBase64String(ms.ToArray()) ;
            }
        }

1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

-2

你可以使用这段代码:不应该使用这段代码:

byte[] bytes = streamReader.CurrentEncoding.GetBytes(streamReader.ReadToEnd());

请查看此答案的评论,了解原因。我将保留答案,以便人们了解这种方法存在的问题,因为直到现在我都没有这样做。

14
这是错误的。(所以停止点赞它!)首先将字节转换为文本,然后再转回字节。除了映射效率低之外,它也可能非同构(例如,不是每个字节序列都是每种编码中的有效字符序列)。因此,这可能会破坏字节序列或导致错误。这不是读取字节的适当方式。 - Konrad Rudolph
9
顺便说一句,更新的答案作为FYI是可以接受的,所以没有必要downvote它... - Konrad Rudolph
1
@KonradRudolph 如果我们对每个问题都回答什么不要做,很快就会变得一团糟,因为每个问题可能有成千上万个这样的有效答案。因此,我认为仅仅回答“不要做这件特定的事情”是没有价值的。 - julealgon
@julealgon 是的,我现在同意了。 - Konrad Rudolph

-5

对于那些建议获取字节并将其复制到MemoryStream等的人-如果内容不会比计算机内存允许的更大,为什么不直接使用StreamReader的内置ReadLine()ReadToEnd()呢?我看到这些甚至没有被提到,它们可以为您完成所有操作。

我有一个用例,我只想存储用户在同步/初始化过程中选择的SQLite文件的路径,从FileDialogResult中。然后,当程序运行正常应用程序进程时,我的程序稍后需要使用此路径。也许不是捕获/重用信息的理想方式,但与编写/读取.ini文件没有什么区别-我只是不想为一个值设置一个.ini文件。所以我只是从一个平面的单行文本文件中读取它。这是我做的:

string filePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (!filePath.EndsWith(@"\")) temppath += @"\"; // ensures we have a slash on the end
filePath = filePath.Replace(@"\\", @"\"); // Visual Studio escapes slashes by putting double-slashes in their results - this ensures we don't have double-slashes
filePath += "SQLite.txt";

string path = String.Empty;
FileStream fs = new FileStream(filePath, FileMode.Open);
StreamReader sr = new StreamReader(fs);
path = sr.ReadLine();  // can also use sr.ReadToEnd();
sr.Close();
fs.Close();
fs.Flush();

return path;

如果你真的需要一个 byte[] 而不是一个 string,那么可以使用我的示例进行转换:
byte[] toBytes;
FileStream fs = new FileStream(filePath, FileMode.Open);
StreamReader sr = new StreamReader(fs);
toBytes = Encoding.ASCII.GetBytes(path);
sr.Close();
fs.Close();
fs.Flush();

return toBytes;

(返回toBytes而不是path。)

如果您不想使用ASCII,可以轻松地将其替换为UTF8Unicode等。


1
抱歉,我不明白您的请求。请提供需要翻译的具体内容。 - Mike Keskinov
@xyz 你说得对,但这取决于开发人员知道他们的输入是什么并为其调整编码,这是首要的。在这个问题的OP中没有提供关于数据读取来源或格式的大量信息,因此为了构建答案,我不得不假设它是文本文件中的文本,并且甚至(作为奖励!)包括有关获取字节的那一部分。如果是字节,则当然不仅仅是Encoding.ASCII.GetBytes(),还有Encoding.UTF8.GetBytes()Encoding.Unicode.GetBytes()等。在批评答案之前,请使用智能感知。 - vapcguy
@mjwills 唯一不行的情况是当你有一个超过系统内存(RAM)的文件时,所以在99.9%的情况下它都能工作。我很难相信程序员每天都会有这样的文件,但如果他们确实有,他们可以创建一个缓冲系统,就像接受的答案中所述。我不想重复已经提供的内容。 - vapcguy
你不能把二进制文件当作文本文件来处理,然后说它会在99.9%的情况下工作。对于特定的文件类型,它确实可以在99.9%的情况下工作。但是,根据定义,这比使用一种始终有效的技术更不合适。就像我之前的评论所说的那样。 - mjwills
1
@vapcguy 好的,祝你有美好的一天。 - mjwills
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接