为了账号安全,请及时绑定邮箱和手机立即绑定

在没有 LINQ 的情况下创建在 HTTP (< 2.0) 字符串中找到的标头的字典查找

在没有 LINQ 的情况下创建在 HTTP (< 2.0) 字符串中找到的标头的字典查找

C#
拉风的咖菲猫 2023-05-14 16:09:03
我目前正在从日志文件中解析一些 HTTP 请求标头,我需要将它们拆分并创建一个字典以便于查找。我正在使用的代码是:public static Dictionary<string, string> CreateLookupDictionary(string input)    {        Debug.WriteLine(input);        return input.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)            .Select(x => x.Split(new string[] {": "}, StringSplitOptions.None))            .ToDictionary(x => x[0], x => x[1], StringComparer.InvariantCultureIgnoreCase);    }这适用于 99% 的标头,但随后.........Keep-Alive: timeout=20Expires: Sat, 04 Jun 2011 18:43:08 GMTCache-Control: max-age=31536000Cache-Control: publicAccept-Ranges: bytes...现在密钥Cache-Control已经存在,因此它会抛出有关密钥已存在的异常。有没有一种优雅的方法来覆盖那里的价值,除非我真的必须这样做,否则我不想重写 LINQ。
查看完整描述

3 回答

?
HUWWW

TA贡献1874条经验 获得超12个赞

  • .ToDictionary要求每一个key都是独一无二的,通过设计。

  • Linq 没有.DistinctBy( x => x.y )方法,但我们可以使用.GroupBy( x => x.y ).Select( grp => grp.Last() ). 这具有丢弃具有相同值的所有先前结果的效果y

因此,如果您首先按 HTTP 标头名称分组,然后选择每个组中的最后一项,那么这将得到您想要的:

// Using cached static fields to avoid unnecessary array allocation:

static readonly String[] _splitOnLines = new String[] { "\r\n" };

static readonly String[] _splitHeader  = new String[] { ": " };


public static Dictionary<String,String> CreateLookupDictionary(String input)

{

    Debug.WriteLine(input);

    return input

        .Split( _splitOnLines , StringSplitOptions.RemoveEmptyEntries )

        .Select( line => line.Split( _splitHeader, StringSplitOptions.None ) )

        .Where( arr => arr.Length == 2 ) // filter out invalid lines, if any

        .Select( arr => ( name: arr[0], value: arr[1] ) ) // using C# 7 named tuples for maintainability

        .GroupBy( header => header.name )

        .Select( duplicateHeaderGroup => duplicateHeaderGroup.Last() )

        .ToDictionary( header => header.name, header.value, StringComparer.InvariantCultureIgnoreCase );

}

或者,使用自定义聚合,该聚合使用始终成功的键索引Item设置器属性。如果与我之前的示例相比很少有重复项,则此方法可能具有更快的性能。


public static Dictionary<String,String> CreateLookupDictionary(String input)

{

    Debug.WriteLine(input);

    return input

        .Split( _splitOnLines , StringSplitOptions.RemoveEmptyEntries )

        .Select( line => line.Split( _splitHeader, StringSplitOptions.None ) )

        .Where( arr => arr.Length == 2 )

        .Select( arr => ( name: arr[0], value: arr[1] ) )

        .Aggregate(

            new Dictionary<String,String>( StringComparer.InvariantCultureIgnoreCase ),

            ( d, header ) =>

            {

                d[ header.name ] = header.value;

                return d;

            }

        );

}


查看完整回答
反对 回复 2023-05-14
?
狐的传说

TA贡献1804条经验 获得超3个赞

.Net Core 2.0 有解决办法:

使用 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser 类:

如果您使用这种方法,您将能够处理许多无法预料的边缘情况。

这是伪代码(未经测试)——这只适用于 .Net Core(显然)

using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;

using System;

using System.Buffers;

using System.Collections.Generic;

using System.Text;


public class ExampleUsage 

{

    public static void Main(string[] args)

    {

        string requestString =

        @"POST /resource/?query_id=0 HTTP/1.1

        Host: example.com

        User-Agent: custom

        Accept: */*

        Connection: close

        Content-Length: 20

        Content-Type: application/json


        {""key1"":1, ""key2"":2}";


        var headerResult = Parser.Parse(requestString);

    }

}


public class Parser : IHttpHeadersHandler

{

    private Dictionary<string, string> result = null;


    public Dictionary<string, string> Parse(string requestString)

    {

        result = new Dictionary<string, string>();  


        byte[] requestRaw = Encoding.UTF8.GetBytes(requestString);

        ReadOnlySequence<byte> buffer = new ReadOnlySequence<byte>(requestRaw);

        HttpParser<Program> parser = new HttpParser<Program>();


        parser.ParseRequestLine(this, buffer, out var consumed, out var examined);

        buffer = buffer.Slice(consumed);


        parser.ParseHeaders(this, buffer, out consumed, out examined, out var b);

        buffer = buffer.Slice(consumed);

    }


    public void OnHeader(Span<byte> name, Span<byte> value)

    {

        result.Add(Encoding.UTF8.GetString(name), Encoding.UTF8.GetString(value));

    }

}


查看完整回答
反对 回复 2023-05-14
?
慕尼黑8549860

TA贡献1818条经验 获得超11个赞

就我个人而言,我会使用它.ToLookup来保留同一个键的多个值。 .ToLookup不会在重复键上出错,它会创建一个IEnumerable<V>值,这种情况是IEnumerable<string>:


public static ILookup<string, string> CreateLookup(string input)

{

    Debug.WriteLine(input);

    return input.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)

        .Select(x => x.Split(new string[] {": "}, StringSplitOptions.None))

        .ToLookup(x => x[0], x => x[1], StringComparer.InvariantCultureIgnoreCase);

}


查看完整回答
反对 回复 2023-05-14
  • 3 回答
  • 0 关注
  • 117 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信