简体   繁体   中英

C# split string to array then to key value pair in dictionary

I'm currently passing a string, with multiple delimiters, to a method that I would like to initially split (at ~) into a string array, then each entry in the array (at |) to a dictionary ("key"="value").

~timestamp=Mar  1 2018  3:14PM|proc=procedure A|tran=transaction 1|rowCount=987
~timestamp=Mar  1 2018  3:14PM|proc=procedure B|tran=transaction 2|rowCount=654
~timestamp=Mar  1 2018  3:14PM|proc=procedure C|tran=transaction 3|rowCount=321

I can accomplish the initial split into individual "rows"

~timestamp=Mar 1 2018 3:14PM|proc=procedure A|tran=transaction 1|rowCount=987

with:

string result = <<long string>>;
string filepath = <<filepath variable>>;
string[] resultRows = result.Split('~');

try
{
    foreach (string row in resultRows)
    {
        if (!File.Exists(filepath))
        {
            using (StreamWriter log = File.CreateText(filepath))
            {
                log.WriteLine(row);
            }
        }
        else
        {
            using (StreamWriter log = File.AppendText(filepath))
            {
                log.WriteLine(row);
            }
        }
    }
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
}

However, when I try to then parse each of those "rows" to a Dictionary, an error (Index was outside the bounds of the array) is thrown.

string result = <<long string>>;
string filepath = <<filepath variable>>;
string[] resultRows = result.Split('~');
Dictionary<string, string> dict = new Dictionary<string, string>();

try
{
    foreach (string row in resultRows)
    {
        var results = row.Split('|').Select(s => s.Split(new[] { '=' }));

        foreach (var item in results)
        {
            dict.Add(item[0], item[1]);
        }
    }
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
}

I'm assuming that my expectations of the dictionary aren't correct -- I would like to see something along the lines of the following, for each "entry" in the Dictionary:

~timestamp=>Mar 1 2018 3:14PM, proc=>procedureA, tran=>transaction1, rowCount=>987

so that I can then write each line to a log file, where I can access the value for each key individually. If my understanding of how to set/get values in a dictionary are way off, is there a better way to accomplish this task?

Main problem was possible empty split-parts and after further splitting trying to access them via index in dict.Add(item[0], item[1]); - if there is only one result its an index out of bounds exception.

Fixed version using IEnumerable.Aggregate() on your split-at ~ array:

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main(string[] args)
    {
        var dd = new Dictionary<string, Dictionary<string, string>>();
        var data = 
        "~timestamp=Mar 1 2018 3:14PM|proc=procedure A|tran=transaction 1|rowCount=987" +
        "~timestamp=Mar 1 2018 3:14PM|proc=procedure B|tran=transaction 2|rowCount=654" +
        "~timestamp=Mar 1 2018 3:14PM|proc=procedure C|tran=transaction 3|rowCount=321";


        // splitting is done here:

        var d = data  
            .Split(new[] { '~' }, StringSplitOptions.RemoveEmptyEntries) 
            .Aggregate(new List<Dictionary<string, string>>(), 
            (listOfDicts, part) =>
            {
                var key = Guid.NewGuid().GetHashCode().ToString();
                listOfDicts.Add(new Dictionary<string, string>());

                foreach (var p in part.Split(new[] { '|' },
                                             StringSplitOptions.RemoveEmptyEntries))
                {
                    // limit split to 2 parts if more then 1 = inside
                    var kvp = p.Split(new[] { '=' }, 2,
                                      StringSplitOptions.RemoveEmptyEntries);

                    // either 2 or 1 is the result, handle both:
                    if (kvp.Count() == 2)
                        listOfDicts.Last()[kvp[0]] = kvp[1];
                    else
                        listOfDicts.Last()[kvp[0]] = null;
                }
                return listOfDicts;
            });


        // output:

        foreach (var k in d)
        {
            Console.WriteLine();
            foreach (var innerKvp in k)
            {
                Console.WriteLine("    " + innerKvp.Key + "    " + innerKvp.Value);
            }
        }
        Console.ReadLine();
    }
}

Output: (list of dictionaries)

timestamp    Mar 1 2018 3:14PM
proc    procedure A
tran    transaction 1
rowCount    987

timestamp    Mar 1 2018 3:14PM
proc    procedure B
tran    transaction 2
rowCount    654

timestamp    Mar 1 2018 3:14PM
proc    procedure C
tran    transaction 3
rowCount    321

The following function should be able to parse your inputs properly:

private static List<Dictionary<String,String>> ParseData(String data)  
{
    String[] entriesData = data.Split(new Char[] { '~' },StringSplitOptions.RemoveEmptyEntries);
    List<Dictionary<String,String>> result = new List<Dictionary<String,String>>(entriesData.Length);

    foreach (String entryData in entriesData)
    {
        String[] entryDataPairs = entryData.Split(new Char[] { '|' },StringSplitOptions.RemoveEmptyEntries);
        Dictionary<String,String> entryResult = new Dictionary<String,String>(entryDataPairs.Length);

        foreach (String entryDataPair in entryDataPairs)
        {
            String[] kvp = entryDataPair.Split(new Char[] { '=' },StringSplitOptions.RemoveEmptyEntries);
            entryResult.Add(kvp[0], kvp[1]);
        }

        result.Add(entryResult);
    }

    return result;
}

The result produced by the following input string:

~timestamp=Mar  1 2018  3:14PM|proc=procedure A|tran=transaction 1|rowCount=987~timestamp=Mar  1 2018  3:14PM|proc=procedure B|tran=transaction 2|rowCount=654~timestamp=Mar  1 2018  3:14PM|proc=procedure C|tran=transaction 3|rowCount=321

is:

--- ENTRY ---
timestamp = Mar  1 2018  3:14PM
proc = procedure A
tran = transaction 1
rowCount = 987

--- ENTRY ---
timestamp = Mar  1 2018  3:14PM
proc = procedure B
tran = transaction 2
rowCount = 654

--- ENTRY ---
timestamp = Mar  1 2018  3:14PM
proc = procedure C
tran = transaction 3
rowCount = 321

Give it a try by visiting this link !

Try something like this:

var dictionary = new Dictionary<string, string>();

foreach (var resultRow in resultRows)
{
    if(!string.IsNullorEmpty(resultRow))
    {
        var rowItems = resultRow.Split('|').ToList();

        dictionary.Add(rowItems.First(), string.Join("|", rowItems.Skip(1));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM