【问题标题】:Select two characters from string in LINQ从 LINQ 中的字符串中选择两个字符
【发布时间】:2018-12-12 11:59:53
【问题描述】:

我必须将以0x 开头的十六进制字符串或以BitConverter 转换的十六进制字符串转换为byte 数组。为此,我使用这个功能非常好:

public static byte[] ConvertToByteArray(this string s)
{
    if (s.StartsWith("0x"))
    {
        var ret = new byte[(s.Length - 2) / 2];
        for (int i = 2; i < s.Length; i += 2)
        {
            ret[(i - 2) / 2] = Convert.ToByte(string.Concat(s[i], s[i + 1]), 16);
        }
        return ret;
    }
    else
        return s.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
}

示例输入,来自某种网络设备(将其视为使用 wireshark 记录的消息):

byte[] data1 = "0x020206000000022800A601585E40".ConvertToByteArray();
byte[] data2 = "02-02-06-00-00-00-02-28-00-A6-01-58-5E-40".ConvertToByteArray();
CollectionAssert.AreEqual(data1, data2);

现在我想了解如何在 LINQ 中编写第一种可能性(以 0x 开头)来摆脱这个 1990 年的 for 循环。

有没有办法同时选择两个字符,或者有比我的更优雅的方法吗?

【问题讨论】:

  • 你能分享函数 ConvertToByteArray 的输入吗?
  • 尝试以下操作:字符串输入 = "0x0123456789ABCDEF"; ushort[] output = input.Skip(2).Select((x, i) => new { x = x, i = i }).GroupBy(x => xi / 2).Select(x => (ushort )((x.FirstOrDefault().x
  • 或者如果您需要将字符解析为十六进制: string input = "0x0123456789ABCDEF"; ushort[] output = input.Skip(2).Select((x, i) => new { x = byte.Parse(x.ToString(), System.Globalization.NumberStyles.HexNumber), i = i })。 GroupBy(x => xi / 2).Select(x => (ushort)((x.FirstOrDefault().x
  • @jdweng 这对我不起作用,因为我需要一个字节数组。数据形成网络资源
  • 我只是按照您从字符串到 ushort 的代码进行操作。不知道为什么你的代码有两个步骤去 ushort 然后去 byte[]。我以为你需要 ed ushort。

标签: c# arrays string linq


【解决方案1】:

这是你的循环的 linq 等价物,不管其他任何考虑:

if (s.StartsWith("0x"))
{
    return
    s.Skip(2)
     .Select((x,i) => new {index = i, value = x})
     .GroupBy(pair => pair.index / 2)
     .Select(grp => string.Join("", grp.Select(x=>x.value)))
     .Select(x => Convert.ToByte(x,16))
     .ToArray();
}

但这似乎是您考虑没有 90ish 代码的解决方案:

public static byte[] ConvertToByteArray(this string s)
{
    string tmp = s.Replace("0x","").Replace("-","");
    tmp = Regex.Replace(tmp, ".{2}", "$0-");
    return tmp.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
}

【讨论】:

    【解决方案2】:

    我想你可以先看看这个

    使用@jdweng 示例输入。

    string input = "0x0123456789ABCDE".Replace("0x", string.Empty);
    long intValue = long.Parse(s, System.Globalization.NumberStyles.HexNumber);
    

    现在,如果您有很长的时间,您可以轻松地将其转换为 byte[]。

    byte[] array = BitConverter.GetBytes(intValue);
    

    我知道这不是针对您的问题的 LINQ 解决方案,但它简洁明了。

    【讨论】:

    • 请记住,可以正确解析为 int 的最大十六进制数是“7FFFFFFF”。取决于您的需要,我会考虑使用 long.Parse,它允许您使用更大的十六进制数字。除此之外,我认为这是解决这个问题的最佳方案。
    • 您还可以测试 jdweng 在问题中的评论,如果可行,请将其添加到您的问题中(引用他/她的话)。
    • 由于输入是某种网络流量(所以它的长度变化很大)这对我不起作用
    【解决方案3】:

    更新:

    阅读 OP 的评论后,我看到他希望能够使用任意长度的十六进制字符串。我很想使用迭代器函数来返回您的十六进制对,以匹配您的拆分结果。然后,您可以通过相同的转换提供可枚举的任何一个,如下所示:

    public byte[] ConvertToByteArray(string s)
    {
        IEnumerable<string> query = Enumerable.Empty<string>();
    
        if (s.StartsWith("0x"))
        {
            query = IterateHexPairs(s.Substring(2));
        }
        else
        {
            query = s.Split('-');
        }
    
        return query.Select(b => Convert.ToByte(b, 16)).ToArray();
    
        IEnumerable<string> IterateHexPairs(string hexLiteral)
        {
            char? previousNibble = null;
    
            foreach (var nibble in hexLiteral)
            {
                if (previousNibble != null)
                {
                    yield return new string(new char[] { previousNibble.Value, nibble });
                    previousNibble = null;
                }
                else
                {
                    previousNibble = nibble;   
                }                               
            }
        }
    }
    

    这避免了重复您的转换逻辑,因为它们都从 IEnumerable 获取。唯一的区别是 IEnumerable 的来源。更改为您提供您认为合适的可枚举的代码。我认为 Iterator 函数更易于维护,但您可以通过 Linq 查询来实现相同的结果,如下所示:

    public byte[] ConvertToByteArray(string s)
    {
        IEnumerable<string> query = Enumerable.Empty<string>();
    
        if (s.StartsWith("0x"))
        {        
            // omit the 0x
            query = s.Skip(2)
            // get the char and index, so we can pair them up
                     .Select((c, i) => new { Char = c, Index = i })
            // group them into pairs
                     .GroupBy(o => o.Index / 2)
            // select them as new strings, so they can be converted
                     .Select(g => new string(g.Select(o => o.Char).ToArray()));
        }
        else
        {
            query = s.Split('-');
        }
    
        return query.Select(b => Convert.ToByte(b, 16)).ToArray();
    }
    

    【讨论】:

    • 我想使用低级信息,因为它是低级输入源(网络流量)
    • @MaxR。我已经更新了我的答案以解决您的反馈。现在它将采用任意长度的十六进制字符串。我已经说明了 2 种方法,一种使用 Linq,另一种使用 Iterator 函数。
    猜你喜欢
    • 1970-01-01
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多