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

我目前正在从日志文件中解析一些 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=20

Expires: Sat, 04 Jun 2011 18:43:08 GMT

Cache-Control: max-age=31536000

Cache-Control: public

Accept-Ranges: bytes

...

现在密钥Cache-Control已经存在,因此它会抛出有关密钥已存在的异常。


有没有一种优雅的方法来覆盖那里的价值,除非我真的必须这样做,否则我不想重写 LINQ。


拉风的咖菲猫
浏览 162回答 3
3回答

HUWWW

.ToDictionary要求每一个key都是独一无二的,通过设计。Linq 没有.DistinctBy( x => x.y )方法,但我们可以使用.GroupBy( x => x.y ).Select( grp => grp.Last() ).&nbsp;这具有丢弃具有相同值的所有先前结果的效果y。因此,如果您首先按 HTTP 标头名称分组,然后选择每个组中的最后一项,那么这将得到您想要的:// Using cached static fields to avoid unnecessary array allocation:static readonly String[] _splitOnLines = new String[] { "\r\n" };static readonly String[] _splitHeader&nbsp; = new String[] { ": " };public static Dictionary<String,String> CreateLookupDictionary(String input){&nbsp; &nbsp; Debug.WriteLine(input);&nbsp; &nbsp; return input&nbsp; &nbsp; &nbsp; &nbsp; .Split( _splitOnLines , StringSplitOptions.RemoveEmptyEntries )&nbsp; &nbsp; &nbsp; &nbsp; .Select( line => line.Split( _splitHeader, StringSplitOptions.None ) )&nbsp; &nbsp; &nbsp; &nbsp; .Where( arr => arr.Length == 2 ) // filter out invalid lines, if any&nbsp; &nbsp; &nbsp; &nbsp; .Select( arr => ( name: arr[0], value: arr[1] ) ) // using C# 7 named tuples for maintainability&nbsp; &nbsp; &nbsp; &nbsp; .GroupBy( header => header.name )&nbsp; &nbsp; &nbsp; &nbsp; .Select( duplicateHeaderGroup => duplicateHeaderGroup.Last() )&nbsp; &nbsp; &nbsp; &nbsp; .ToDictionary( header => header.name, header.value, StringComparer.InvariantCultureIgnoreCase );}或者,使用自定义聚合,该聚合使用始终成功的键索引Item设置器属性。如果与我之前的示例相比很少有重复项,则此方法可能具有更快的性能。public static Dictionary<String,String> CreateLookupDictionary(String input){&nbsp; &nbsp; Debug.WriteLine(input);&nbsp; &nbsp; return input&nbsp; &nbsp; &nbsp; &nbsp; .Split( _splitOnLines , StringSplitOptions.RemoveEmptyEntries )&nbsp; &nbsp; &nbsp; &nbsp; .Select( line => line.Split( _splitHeader, StringSplitOptions.None ) )&nbsp; &nbsp; &nbsp; &nbsp; .Where( arr => arr.Length == 2 )&nbsp; &nbsp; &nbsp; &nbsp; .Select( arr => ( name: arr[0], value: arr[1] ) )&nbsp; &nbsp; &nbsp; &nbsp; .Aggregate(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new Dictionary<String,String>( StringComparer.InvariantCultureIgnoreCase ),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ( d, header ) =>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; d[ header.name ] = header.value;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return d;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; );}

狐的传说

.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));    }}

慕尼黑8549860

就我个人而言,我会使用它.ToLookup来保留同一个键的多个值。 .ToLookup不会在重复键上出错,它会创建一个IEnumerable<V>值,这种情况是IEnumerable<string>:public static ILookup<string, string> CreateLookup(string input){&nbsp; &nbsp; Debug.WriteLine(input);&nbsp; &nbsp; return input.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)&nbsp; &nbsp; &nbsp; &nbsp; .Select(x => x.Split(new string[] {": "}, StringSplitOptions.None))&nbsp; &nbsp; &nbsp; &nbsp; .ToLookup(x => x[0], x => x[1], StringComparer.InvariantCultureIgnoreCase);}
打开App,查看更多内容
随时随地看视频慕课网APP