将字符串列表转换为字节数组

3

我正在尝试编写一个方法,该方法接受一个 List<string>,然后将整个列表转换为一个大的字节数组。就像这样:

private byte[] ConvertStringsToBytes(List<string> list)
{
    List<byte> byteList = new List<byte>();

    foreach (var i in list)
    {
        byteList.Add(Encoding.UTF8.GetBytes(i));
    }

    return byteList.ToArray();
}

然而我得到了以下错误信息:

参数类型“byte[]”无法分配给“byte”的参数类型,在byteList.Add(Encoding.UTF8.GetBytes(i))上

我错在哪里?如何正确地将此列表转换为一个字节数组?

7
使用 AddRange(),而不是 Add - Disappointed
4个回答

7
更有效的方法是先将字符串连接起来,然后像这样将其转换为字节数组:

List<string> input = new List<string> { "first", "second" };
string fullString = String.Join(String.Empty, list.ToArray());
byte[] byteArray = Encoding.UTF8.GetBytes(fullString);

如果性能很重要,而且列表中有很多字符串,您可以采用以下方法: 编辑:经过基准测试,此方法确实比上述方法慢。
List<string> input = new List<string> { "first", "second" };
StringBuilder sb = new StringBuilder();
foreach (string s in input )
    sb.Append(s);

byte[] byteArray = Encoding.UTF8.GetBytes(sb.ToString());

编辑: 我对本帖中提到的一些方法进行了基准测试。以下是发布版本的输出结果:

ConvertWithString         896ms
ConvertWithStringBuilder  858ms
ConvertWithConcat        1529ms
ConvertWithSelectMany    2234ms
ConvertWithBuffer         904ms

ConvertWithString         501ms
ConvertWithStringBuilder  919ms
ConvertWithConcat        1435ms
ConvertWithSelectMany    2044ms
ConvertWithBuffer         636ms

如果你没有很多字符串,性能似乎并不重要。

以下是代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    internal class Program
    {
        static byte[] ConvertWithBuffer(List<string> list)
        {
            int totalSize = list.Sum(x => Encoding.UTF8.GetByteCount(x));
            byte[] buffer = new byte[totalSize];

            int ix = 0;

            foreach (string str in list)
                ix += Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, ix);

            return buffer;
        }

        static byte[] ConvertWithConcat(List<string> list) { return Encoding.UTF8.GetBytes(String.Concat(list)); }

        static byte[] ConvertWithSelectMany(List<string> list)
        {
            return list
                .SelectMany(line => Encoding.UTF8.GetBytes(line))
                .ToArray();
        }

        static byte[] ConvertWithString(List<string> input)
        {
            string fullString = String.Join(String.Empty, input.ToArray());
            return Encoding.UTF8.GetBytes(fullString);
        }

        static byte[] ConvertWithStringBuilder(List<string> input)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string s in input)
                sb.Append(s);

            return Encoding.UTF8.GetBytes(sb.ToString());
        }

        static IEnumerable<string> CreateList()
        {
            for (int i = 0; i < 10000000; i++)
                yield return i.ToString();
        }

        static void Main(string[] args)
        {
            List<string> strings = CreateList().ToList();
            Stopwatch stopWatch = Stopwatch.StartNew();

            // warm up
            ConvertWithString(strings);
            ConvertWithStringBuilder(strings);
            ConvertWithConcat(strings);
            ConvertWithSelectMany(strings);
            ConvertWithBuffer(strings);

            // testing

            stopWatch.Restart();
            ConvertWithString(strings);
            Console.WriteLine("ConvertWithString {0}ms", stopWatch.ElapsedMilliseconds);

            stopWatch.Restart();
            ConvertWithStringBuilder(strings);
            Console.WriteLine("ConvertWithStringBuilder {0}ms", stopWatch.ElapsedMilliseconds);

            stopWatch.Restart();
            ConvertWithConcat(strings);
            Console.WriteLine("ConvertWithConcat {0}ms", stopWatch.ElapsedMilliseconds);

            stopWatch.Restart();
            ConvertWithSelectMany(strings);
            Console.WriteLine("ConvertWithSelectMany {0}ms", stopWatch.ElapsedMilliseconds);

            stopWatch.Restart();
            ConvertWithBuffer(strings);
            Console.WriteLine("ConvertWithBuffer {0}ms", stopWatch.ElapsedMilliseconds);

            Console.WriteLine("press any key...");
            Console.ReadKey();
        }
    }
}

看看你的方法是更快、更慢还是与Dmitry Bychenko的解决方案相同,这将会很有趣。 - Steffen Winkler
我非常确定。看看Xanatos的解决方案,这可能值得与字符串构建器进行基准测试。 - Michael Sander
哦,你真的认为在他的情况下性能很重要吗?!! - Disappointed
1
不,就像上面写的一样。但我很好奇 :) 这只对非常大的列表有影响。对于其他任何东西,我会选择可读性更好的解决方案。 - Michael Sander

3
嗯,类似于这样的(Linq)吗?
private byte[] ConvertStringsToBytes(List<string> list) {
  return list
    .SelectMany(line => Encoding.UTF8.GetBytes(line))
    .ToArray();
}

另一种可能性是:
private byte[] ConvertStringsToBytes(List<string> list) {
  return Encoding.UTF8.GetBytes(String.Concat(list));
}

1
您可以这样做:

private static byte[] ConvertStringsToBytes(List<string> list)
{
    int totalSize = list.Sum(x => Encoding.UTF8.GetByteCount(x));
    byte[] buffer = new byte[totalSize];

    int ix = 0;

    foreach (string str in list)
    {
        ix += Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, ix);
    }

    return buffer;
}

我通过预先计算所需的总大小(在totalSize中)来创建一个单个大缓冲区,然后在foreach循环中填充它。请注意使用ix变量来保存buffer中的当前位置。
与其他方法相比,这种方法的优点是没有字符串或字节数组的复制。UTF8编码的字符串仅在缓冲区buffer中写入一次,不会被复制。

0

List.Add() 只能添加单个元素。您需要将 GetBytes() 的结果放入数组中,循环遍历该数组并将每个单独的数组元素添加到列表中。
也许您可以找到一种方法将 GetBytes 数组转换为列表并使用 AddRange()...

或者您可以在函数中省略所有的 List 并仅使用数组。您可以使用 Array.Resize,但这在性能方面不是最好的选择,因为这会包括大量的值复制。您最好使用一个字节数组的数组,计算所需的最终字节数组大小,然后将每个内部数组的数据复制到您的最终数组中。


ToList()在这里是你的好朋友。 - Steffen Winkler
@SteffenWinkler 这是纯C#还是Linq或其他什么? - Tobias Knauss
1
ToList() 是 Linq 命名空间的一部分,没错。 (而且它非常酷) - Steffen Winkler

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