C# 从分隔字符串中仅提取一个元素的有效方法

C# 从分隔字符串中仅提取一个元素的有效方法,c#,string,C#,String,注意:我在这里也问了同样的问题,但由于有些人将它标记为重复,尽管它有一些巧妙、简洁的解决方案,我不得不创建这个额外的(重复)问题,以使其他面临类似疑问的人更容易回答。添加了基于其他堆栈溢出成员建议的问题 解析大型分隔字符串的有效方法是什么,这样我就可以只访问分隔集中的一个元素,而不必存储涉及的其他子字符串 我特别不想像使用Split()方法那样存储其余的元素值,因为所有这些信息都与当前的问题无关。同样,我也希望通过这样做来节省内存 问题陈述: 给定精确的分隔位置,我需要以最有效的方式从内存消耗和

注意:我在这里也问了同样的问题,但由于有些人将它标记为重复,尽管它有一些巧妙、简洁的解决方案,我不得不创建这个额外的(重复)问题,以使其他面临类似疑问的人更容易回答。添加了基于其他堆栈溢出成员建议的问题

解析大型分隔字符串的有效方法是什么,这样我就可以只访问分隔集中的一个元素,而不必存储涉及的其他子字符串

我特别不想像使用Split()方法那样存储其余的元素值,因为所有这些信息都与当前的问题无关。同样,我也希望通过这样做来节省内存

问题陈述:
给定精确的分隔位置,我需要以最有效的方式从内存消耗和所用时间方面提取该位置中包含的元素

简单示例字符串:“1,2,3,4,…,21,22,23,24”
交货日期:,
分隔位置:22
预期答复:23

另一个示例字符串: “61d2e3f6-bcb7-4cd1-a81e-4f8f497f0da2;0;192.100.0.102:4362;2014-02-14;283;0;354;23;0;;;“0x8D15A2913C934DE”;2014年6月19日星期四22:58:10 GMT;”
分隔符:
分隔位置:7

预期答案:23

的文档中有一些与此问题相关的有用注释,尽管我在发现之前写了以下内容

一种方法是使用方法查找分隔符—您可以指定开始搜索的索引,这样就可以在不必检查每个字符的情况下跳过项目。(对每个角色的检查都是在幕后进行的,但比自己检查要快一点。)

我通过在解决方案中添加一个名为“ExtensionMethods.cs”的新类,创建了一个扩展方法,其内容如下:

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        /// <summary>
        /// Get the nth item from a delimited string.
        /// </summary>
        /// <param name="s">The string to retrieve a delimited item from.</param>
        /// <param name="delimiter">The character used as the item delimiter.</param>
        /// <param name="n">Zero-based index of item to return.</param>
        /// <returns>The nth item or an empty string.</returns>
        public static string Split(this string s, char delimiter, int n)
        {

            int pos = pos = s.IndexOf(delimiter);

            if (n == 0 || pos < 0)
            { return (pos >= 0) ? s.Substring(0, pos) : s; }

            int nDelims = 1;

            while (nDelims < n && pos >= 0)
            {
                pos = s.IndexOf(delimiter, pos + 1);
                nDelims++;
            }

            string result = "";

            if (pos >= 0)
            {
                int nextDelim = s.IndexOf(delimiter, pos + 1);
                result = (nextDelim < 0) ? s.Substring(pos + 1) : s.Substring(pos + 1, nextDelim - pos - 1);
            }

            return result;
        }

    }
}
命名空间扩展方法
{
公共静态类MyExtensions
{
/// 
///从分隔字符串中获取第n项。
/// 
///要从中检索分隔项的字符串。
///用作项分隔符的字符。
///要返回的项的从零开始的索引。
///第n项或空字符串。
公共静态字符串拆分(此字符串s,字符分隔符,int n)
{
int pos=pos=s.IndexOf(分隔符);
如果(n==0 | | pos<0)
{return(pos>=0)?s.Substring(0,pos):s;}
int nDelims=1;
而(nDelims=0)
{
pos=s.IndexOf(分隔符,pos+1);
nDelims++;
}
字符串结果=”;
如果(位置>=0)
{
int-nextDelim=s.IndexOf(分隔符,位置+1);
结果=(nextDelim<0)?s.子字符串(pos+1):s.子字符串(pos+1,nextDelim-pos-1);
}
返回结果;
}
}
}
还有一个小程序来测试它:

using System;
using System.Diagnostics;
using System.Linq;
using ExtensionMethods;

namespace ConsoleApp1
{

    class Program
    {

        static void Main(string[] args)
        {
            // test data...
            string s = string.Join(";", Enumerable.Range(65, 26).Select(c => (char)c));
            s = s.Insert(3, ";;;");

            string o = "";

            Stopwatch sw = new Stopwatch();

            sw.Start();
            for (int i = 1; i <= 1000000; i++) {
                o = s.Split(';', 21);
            }
            sw.Stop();
            Console.WriteLine("Item directly selected: " + sw.ElapsedMilliseconds);

            sw.Restart();
            for (int i = 1; i <= 1000000; i++) {
                o = s.Split(';')[21];
            }
            sw.Stop();
            Console.WriteLine("Item from split array:  " + sw.ElapsedMilliseconds + "\r\n");


            Console.WriteLine(s);
            Console.WriteLine(o);

            Console.ReadLine();

        }
    }
}
使用系统;
使用系统诊断;
使用System.Linq;
使用扩展方法;
名称空间控制台EAPP1
{
班级计划
{
静态void Main(字符串[]参数)
{
//测试数据。。。
strings=string.Join(“;”,Enumerable.Range(65,26)。选择(c=>(char)c));
s=s。插入(3,“;”);
字符串o=“”;
秒表sw=新秒表();
sw.Start();
对于(inti=1;i请尝试以下方法:

public static string MyExtension(this string s, char delimiter, int n)
{
    var begin = n== 0 ? 0 : Westwind.Utilities.StringUtils.IndexOfNth(s, delimiter, n);
    if (begin == -1)
        return null;
    var end = s.IndexOf(delimiter, begin +  (n==0?0:1));
    if (end == -1 ) end = s.Length;
    //var end = Westwind.Utilities.StringUtils.IndexOfNth(s, delimiter, n + 1);
    var result = s.Substring(begin +1, end - begin -1 );

    return result;
}
注:使用的库是西风。实用程序


基准代码:

void Main()
{

     string s = string.Join(";", Enumerable.Range(65, 26).Select(c => (char)c));
            s = s.Insert(3, ";;;");

            string o = "";

            Stopwatch sw = new Stopwatch();

            sw.Start();
            for (int i = 1; i <= 1000000; i++) {
                o = s.Split(';', 21);
            }
            sw.Stop();
            Console.WriteLine("Item directly selected: " + sw.ElapsedMilliseconds);


            sw.Restart();
            for (int i = 1; i <= 1000000; i++) {
                o = s.MyExtension(';', 21);
            }
            sw.Stop();
            Console.WriteLine("Item directly selected by MyExtension: " + sw.ElapsedMilliseconds);

            sw.Restart();
            for (int i = 1; i <= 1000000; i++) {
                o = s.Split(';')[21];
            }
            sw.Stop();
            Console.WriteLine("Item from split array:  " + sw.ElapsedMilliseconds + "\r\n");


            Console.WriteLine(s);
            Console.WriteLine(o);

}

public static class MyExtensions
{
    /// <summary>
    /// Get the nth item from a delimited string.
    /// </summary>
    /// <param name="s">The string to retrieve a delimited item from.</param>
    /// <param name="delimiter">The character used as the item delimiter.</param>
    /// <param name="n">Zero-based index of item to return.</param>
    /// <returns>The nth item or an empty string.</returns>
    public static string Split(this string s, char delimiter, int n)
    {

        int pos = pos = s.IndexOf(delimiter);

        if (n == 0 || pos < 0)
        { return (pos >= 0) ? s.Substring(0, pos) : s; }

        int nDelims = 1;

        while (nDelims < n && pos >= 0)
        {
            pos = s.IndexOf(delimiter, pos + 1);
            nDelims++;
        }

        string result = "";

        if (pos >= 0)
        {
            int nextDelim = s.IndexOf(delimiter, pos + 1);
            result = (nextDelim < 0) ? s.Substring(pos + 1) : s.Substring(pos + 1, nextDelim - pos - 1);
        }

        return result;
    }

    public static string MyExtension(this string s, char delimiter, int n)
    {
        var begin = n== 0 ? 0 : Westwind.Utilities.StringUtils.IndexOfNth(s, delimiter, n);
        if (begin == -1)
            return null;
        var end = s.IndexOf(delimiter, begin +  (n==0?0:1));
        if (end == -1 ) end = s.Length;
        //var end = Westwind.Utilities.StringUtils.IndexOfNth(s, delimiter, n + 1);
        var result = s.Substring(begin +1, end - begin -1 );

        return result;
    }

}
编辑:多亏了@Kalten,我进一步增强了解决方案。在基准测试结果上看到了相当大的差异。

对于“回答”来说太晚了,但这段代码给了我大约0.75秒的运行时间,两个字符串都处理了1000000次。这次的区别是,我现在不是封送对象,而是使用指针

这次我返回一个新字符串(string.Substring)

使用系统;
使用系统诊断;
使用System.Runtime.InteropServices;
班级计划
{
静态void Main(字符串[]参数)
{
字符串testString1=“1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24”;
string testString2=“61d2e3f6-bcb7-4cd1-a81e-4f8f497f0da2;0;192.100.0.102:4362;2014-02-14;283;0;354;23;0;;\“0x8D15A2913C934DE\”,2014年6月19日星期四22:58:10 GMT;
秒表sw=新秒表();
sw.Start();
对于(int i=1;i<1000000;i++)
{
定界(testString1',,22);
定界(testString2’;’,6);
}
sw.Stop();
Console.WriteLine($”==>{sw.elapsedmillesons});
Console.ReadLine();
}
静态字符串分隔符(字符串stringUnderTest、字符分隔符、int skipCount)
{
const int SIZE_OF_UNICHAR=2;
int i=0;
int指数=0;
char c=char.MinValue;
GCHandle handle=GCHandle.Alloc(stringUnderTest,GCHandleType.pinted);
尝试
{
IntPtr ptr=handle.addrofPindedObject();
对于(i=0;i>1,(i-index-SIZE\u OF UNICHAR)>>1);
}
}

使用以下正则表达式:
^([^;]*;){21}(.*);
,这样您就不必生成孔拆分列表来搜索所需的位置,一旦到达该位置,将取决于是否存在

说明

^ --> start of a line.

([^;]*;){Position - 1} --> notice that the symbol ; here is the delimiter, the expression will loop Pos - 1 times

(.*?) --> Non-Greedy .*

<
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        string testString1 = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24";
        string testString2 = "61d2e3f6-bcb7-4cd1-a81e-4f8f497f0da2;0;192.100.0.102:4362;2014-02-14;283;0;354;23;0;;;\"0x8D15A2913C934DE\";Thursday, 19-Jun-14 22:58:10 GMT;";

        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 1; i < 1000000; i++)
        {
            Delimit(testString1, ',', 22);
            Delimit(testString2, ';', 6);
        }
        sw.Stop();
        Console.WriteLine($"==>{sw.ElapsedMilliseconds}");
        Console.ReadLine();
    }

    static string Delimit(string stringUnderTest, char delimiter, int skipCount)
    {
        const int SIZE_OF_UNICHAR = 2;

        int i = 0;
        int index = 0;
        char c = Char.MinValue;

        GCHandle handle = GCHandle.Alloc(stringUnderTest, GCHandleType.Pinned);
        try
        {
            IntPtr ptr = handle.AddrOfPinnedObject();
            for (i = 0; i < skipCount; i++)
                while ((char)Marshal.ReadByte(ptr, index += SIZE_OF_UNICHAR) != delimiter) ;
            i = index;
            while ((c = (char)Marshal.ReadByte(ptr, i += SIZE_OF_UNICHAR)) != delimiter) ;
        }
        finally
        {
            if (handle.IsAllocated)
                handle.Free();
        }

        return stringUnderTest.Substring((index + SIZE_OF_UNICHAR) >> 1, (i - index - SIZE_OF_UNICHAR) >> 1);
    }
}
^ --> start of a line.

([^;]*;){Position - 1} --> notice that the symbol ; here is the delimiter, the expression will loop Pos - 1 times

(.*?) --> Non-Greedy .*
Console.WriteLine("First Delimiter : ");
        int Position = 22;
        char delimiter = ',';
        string pattern = @"^([^" + delimiter + "]*" + delimiter + "){" + (Position - 1) + @"}(.*?)" + delimiter;
        Regex regex = new Regex(pattern, RegexOptions.Singleline);
        // First Example
        string Data = @"AAV,zzz,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22ABC,23,24,24";
        Match Re = regex.Match(Data);
        if (Re.Groups.Count > 0)
            Console.WriteLine("\tMatch found : " + Re.Groups[2]);


        // Second Example
        Console.WriteLine("Second Delimiter : ");
        Position = 8;
        delimiter = ';';
        pattern = @"^([^" + delimiter + "]*" + delimiter + "){" + (Position - 1) + @"}(.*?)" + delimiter;
        Data = @"61d2e3f6-bcb7-4cd1-a81e-4f8f497f0da2;0;192.100.0.102:4362;2014-02-14;283;0;354;23;0;;;""0x8D15A2913C934DE"";Thursday, 19-Jun-14 22:58:10 GMT;";
        regex = new Regex(pattern, RegexOptions.Singleline);
        Re = regex.Match(Data);
        if (Re.Groups.Count > 0)
            Console.WriteLine("\tMatch found : " + Re.Groups[2]);
    Match found : 22ABC
    Match found : 23
static public IEnumerable<char> GetDelimitedField(this IEnumerable<char> source, char delimiter, int index)
{
    foreach (var c in source)
    {
        if (c == delimiter) 
        {
            if (--index < 0) yield break;
        }
        else
        {
            if (index == 0) yield return c;
        }
    }
}
static public string GetDelimitedString(this string source, char delimiter, int index)
{
    var result = source.GetDelimitedField(delimiter, index);
    return new string(result.ToArray());
}
var input ="Zero,One,Two,Three,Four,Five,Six";
var output = input.GetDelimitedString(',',5);
Console.WriteLine(output);
Five