【问题标题】:Converting a list of strings to an array of bytes将字符串列表转换为字节数组
【发布时间】:2015-08-13 07:27:18
【问题描述】:

我正在尝试编写一个接受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' on byteList.Add(Encoding.UTF8.GetBytes(i));

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

【问题讨论】:

  • 使用AddRange(),而不是Add

标签: c# arrays list encoding


【解决方案1】:

更有效的方法是先将字符串连接在一起,然后将其转换为字节数组,如下所示:

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's solution相同会很有趣。
  • 我很确定它是。看看 xanatos 解决方案,这可能是对字符串生成器的基准测试。
  • 哦,你真的认为他的表现真的很重要吗??!!
  • 不,就像上面写的那样。但我很好奇 :) 它只对非常大的列表很重要。对于其他任何事情,我都会根据它的可读性来选择解决方案。
【解决方案2】:

呃,像这样的(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));
}

【讨论】:

    【解决方案3】:

    你可以这样做:

    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 中)创建一个大buffer,然后将其填充到foreach 循环中。注意使用ix变量将当前位置保存在buffer中。

    与其他方法相比,此方法的优势在于无需复制字符串或字节数组。 UTF8 编码的字符串在buffer 缓冲区中只写入一次,不会被复制。

    【讨论】:

      【解决方案4】:

      List.Add() 允许您仅添加一个元素。您需要将GetBytes() 结果放入一个数组中,遍历该数组并将每个数组元素添加到列表中。
      也许你找到一种方法将 GetBytes 数组转换为列表并使用AddRange()...

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

      【讨论】:

      • ToList() 是你的朋友。
      • @SteffenWinkler 这是普通的 c# 还是 Linq 或其他什么?
      • ToList() 是 Linq 命名空间的一部分,是的。 (而且很酷)
      猜你喜欢
      • 1970-01-01
      • 2014-11-08
      • 1970-01-01
      • 2013-04-21
      • 2021-11-11
      • 1970-01-01
      • 2021-01-12
      • 1970-01-01
      • 2016-05-05
      相关资源
      最近更新 更多