【问题标题】:string split by index / params?字符串按索引/参数拆分?
【发布时间】:2011-08-22 14:11:24
【问题描述】:

就在我编写自己的函数之前,只是想检查.NET 库中是否存在类似string.split(string input, params int[] indexes) 的函数? 这个函数应该在我传递给它的索引上拆分字符串。

编辑:我不应该添加 string.join 句子 - 它令人困惑。

【问题讨论】:

  • 我假设该方法应该在每个索引元素处拆分输入字符串?所以string.Split("1234567", 2, 5) == { "12", "345", "67" }?如果是这样,就没有这样的方法。如果没有,您能详细说明吗?
  • 您到底想完成什么? Split 和 Join 有非常不同的结果,让您的问题变得混乱。
  • 没错,dlev。我认为这个线程产生的敌意答案是否定的。
  • @maxp 与此类似的问题通常是提问者懒惰的结果。在这种情况下,我将其解释为“我似乎找不到;有人可以在我写之前确认吗?”无论如何,基类库中不存在这样的函数,但幸运的是,编写自己的函数非常简单。祝你好运!
  • maxp:这不是敌意——但你的问题本可以更好/更清楚,例如通过包含一个示例,说明您希望如何调用该方法以及它应该返回什么。

标签: c# string split


【解决方案1】:

您可以使用String 实例方法Substring

string a = input.Substring(0, 10);
string b = input.Substring(10, 5);
string c = input.Substring(15, 3);

【讨论】:

  • 他仍然需要进行多个方法调用;我认为他正在寻找那种精确方法(尽管我很确定这他将如何实施它。因为他必须这样做。)
  • @dlev,我明白你的意思。我不确定采用一组分割点的方法是否会比相应的“长”代码增加很多。
  • 没有这样的内置功能,但它可以作为工具集功能;使用params int[] indexes 参数,这将简单地循环 String.Substring 调用并将结果放入数组中。
  • 您可以使用正则表达式来拆分字符串,并为位置使用量词
【解决方案2】:

所有其他答案似乎都太复杂了,所以我试了一下。

using System.Linq;

public static class StringExtensions
{
    /// <summary>
    ///     Returns a string array that contains the substrings in this instance that are delimited by specified indexes.
    /// </summary>
    /// <param name="source">The original string.</param>
    /// <param name="index">An index that delimits the substrings in this string.</param>
    /// <returns>An array whose elements contain the substrings in this instance that are delimited by one or more indexes.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="index" /> is null.</exception>
    /// <exception cref="ArgumentOutOfRangeException">An <paramref name="index" /> is less than zero or greater than the length of this instance.</exception>
    public static string[] SplitAt(this string source, params int[] index)
    {
        index = index.Distinct().OrderBy(x => x).ToArray();
        string[] output = new string[index.Length + 1];
        int pos = 0;

        for (int i = 0; i < index.Length; pos = index[i++])
            output[i] = source.Substring(pos, index[i] - pos);

        output[index.Length] = source.Substring(pos);
        return output;
    }
}

【讨论】:

  • 很酷的方法。请您对此做一个安全的模拟,如果其中一个索引超出范围,则不会引发异常?还是谢谢。
  • 隐藏异常会破坏惯例。使用字符串时,c# 中的约定是在索引超出范围时抛出错误。而且,由于这更像是一种实用和重用方法,因此我选择为这些异常添加文档。
【解决方案3】:

Split 方法根据识别模式划分字符串。非常适合分解逗号分隔列表等。

但是你是对的,没有内置的字符串方法来实现你想要的。

【讨论】:

    【解决方案4】:

    一种可能的解决方案:

    public static class StringExtension
    {
        public static string[] Split(this string source, params int[] sizes)
        {
            var length = sizes.Sum();
            if (length > source.Length) return null;
    
            var resultSize = sizes.Length;
            if (length < source.Length) resultSize++;
    
            var result = new string[resultSize];
    
            var start = 0;
            for (var i = 0; i < resultSize; i++)
            {
                if (i + 1 == resultSize)
                {
                    result[i] = source.Substring(start);
                    break;
                }
    
                result[i] = source.Substring(start, sizes[i]);
                start += sizes[i];
            }
    
            return result;
        }
    }
    

    【讨论】:

      【解决方案5】:

      这并不能直接回答 您的 笼统问题,而是在最有可能的常见情况下(或者至少在我遇到这个 SO 问题时我正在寻找答案的情况下) ) 其中indexes 是单个int,这种扩展方法比返回string[] 数组要干净一些,尤其是在C# 7 中。

      为了它的价值,我使用string.Substring() 对创建两个char[] 数组、调用text.CopyTo() 并通过调用new string(charArray) 返回两个字符串进行了基准测试。使用 string.Substring() 的速度大约是原来的两倍。

      C# 7 语法

      jdoodle.com example

      public static class StringExtensions
      {
          [MethodImpl(MethodImplOptions.AggressiveInlining)]
          public static (string left, string right) SplitAt(this string text, int index) => 
              (text.Substring(0, index), text.Substring(index));
      }
      
      public static class Program
      {
          public static void Main()
          {
              var (left, right) = "leftright".SplitAt(4);
              Console.WriteLine(left);
              Console.WriteLine(right);
          }
      }
      

      C# 6 语法

      jdoodle.com example

      注意:在 C# 7 之前的版本中使用 Tuple&lt;string, string&gt; 并不会节省太多的冗长性,实际上只返回一个 string[2] 数组可能更简洁。

      public static class StringExtensions
      {
          // I'd use one or the other of these methods, and whichever one you choose, 
          // rename it to SplitAt()
      
          [MethodImpl(MethodImplOptions.AggressiveInlining)]
          public static Tuple<string, string> TupleSplitAt(this string text, int index) => 
              Tuple.Create<string, string>(text.Substring(0, index), text.Substring(index));
      
          [MethodImpl(MethodImplOptions.AggressiveInlining)]
          public static string[] ArraySplitAt(this string text, int index) => 
              new string[] { text.Substring(0, index), text.Substring(index) };
      }
      
      public static class Program
      {
          public static void Main()
          {
              Tuple<string, string> stringsTuple = "leftright".TupleSplitAt(4);
              Console.WriteLine("Tuple method");
              Console.WriteLine(stringsTuple.Item1);
              Console.WriteLine(stringsTuple.Item2);
      
              Console.WriteLine();
      
              Console.WriteLine("Array method");
              string[] stringsArray = "leftright".ArraySplitAt(4);
              Console.WriteLine(stringsArray[0]);
              Console.WriteLine(stringsArray[1]);
          }
      }
      

      【讨论】:

        【解决方案6】:
        public static IEnumerable<string> SplitAt(this string source, params int[] index)
        {
            var indices = new[] { 0 }.Union(index).Union(new[] { source.Length });
        
            return indices
                        .Zip(indices.Skip(1), (a, b) => (a, b))
                        .Select(_ => source.Substring(_.a, _.b - _.a));
        }
        
        var s = "abcd";
        
        s.SplitAt(); // "abcd"
        s.SplitAt(0); // "abcd"
        s.SplitAt(1); // "a", "bcd"
        s.SplitAt(2); // "ab", "cd"
        s.SplitAt(1, 2) // "a", "b", "cd"
        s.SplitAt(3); // "abc", "d"
        

        【讨论】:

        • =&gt; (a, b) 收到错误Predefined type 'System.ValueTuple 2' is not defined or imported
        • @NitinSawant 您需要为 .NET 4.6.2 或更低版本安装 NuGet 包 System.ValueTuple
        【解决方案7】:

        总是有正则表达式。

        这是一个可以扩展的示例:

         string text = "0123456789ABCDEF";
         Match m = new Regex("(.{7})(.{4})(.{5})").Match(text);
         if (m.Success)
         {
             var result = new string[m.Groups.Count - 1];
             for (var i = 1; i < m.Groups.Count; i++)
                 result[i - 1] = m.Groups[i].Value;
         }
        

        这是一个封装了上述逻辑的函数:

            public static string[] SplitAt(this string text, params int[] indexes)
            {
                var pattern = new StringBuilder();
                var lastIndex = 0;
                foreach (var index in indexes)
                {
                    pattern.AppendFormat("(.{{{0}}})", index - lastIndex);
                    lastIndex = index;
                }
                pattern.Append("(.+)");
        
                var match = new Regex(pattern.ToString()).Match(text);
                if (! match.Success)
                {
                    throw new ArgumentException("text cannot be split by given indexes");
                }
        
                var result = new string[match.Groups.Count - 1];
                for (var i = 1; i < match.Groups.Count; i++)
                    result[i - 1] = match.Groups[i].Value;
                return result;            
            }
        

        这篇文章写得很快,但我相信它说明了我的观点,并向评论作者迈克尔强调了我的观点。

        【讨论】:

        • 你好杰克,欢迎来到堆栈溢出。我不同意您的建议,因为它存在一些效率问题,而且这也不是正则表达式的含义。正则表达式用于模式查找,这不是 OP 想要做的。效率方面,您已经迭代了 3 个组 - 只调用 split 3 次不是更合乎逻辑和更有效吗?
        • @Michael:使用正则表达式会带来内容的错误检查。用偏移量手写多行容易出错且乏味。关于效率,如果您指的是性能,我不认为在这种情况下考虑性能是有针对性的。我更关心健壮性和完整性。正如我所说,这是一个可以扩展的示例。可以很容易地将此​​逻辑放入与 OP 调用签名匹配的函数中,从此无需多次调用 substring()。
        • 对这个任务使用 RegEx 是一个非常糟糕的选择。 IMO 将被任何有经验的团队领导或代码审查员拒绝 - 发回以由编码人员重写。结果很难阅读。任何遇到它的人也会感到困惑:提出了它试图做什么的问题;出乎意料。 RegEx 适用于模式识别;不适用于操作字符串索引。
        【解决方案8】:

        以“列表”作为返回的版本。

        来电者

        string iTextLine = "02121AAAARobert Louis StevensonXXXX"
        int[] tempListIndex = new int[] {
            // 0 -  // 0number  (exclude first)
            5,      // 1user
            9,      // 2name
            31      // role
        };  
        
        // GET - words from indexes
        List<string> tempWords = getListWordsFromLine(iTextLine, tempListIndex);
        

        方法

        /// <summary>
        /// GET - split line in parts using index cuts
        /// </summary>
        /// <param name="iListIndex">Input List of indexes</param>
        /// <param name="iTextLine">Input line to split</param>
        public static List<string> getListWordsFromLine(string iTextLine, int[] iListIndex)
        {
            // INIT
            List<string> retObj = new List<string>(); 
            int currStartPos = 0;
            // GET - clear index list from dupl. and sort it
            int[] tempListIndex = iListIndex.Distinct()
                                            .OrderBy(o => o)
                                            .ToArray();
            // CTRL
            if (tempListIndex.Length != iListIndex.Length)
            {
                // ERR
                throw new Exception("Input  iListIndex contains duplicate indexes");
            }
        
        
            for (int jj = 0; jj < tempListIndex.Length; ++jj)
            {
                try
                {
                    // SET - line chunk
                    retObj.Add(iTextLine.Substring(currStartPos,
                                                   tempListIndex[jj] - currStartPos));
                }
                catch (Exception)
                {
                    // SET - line is shorter than expected
                    retObj.Add(string.Empty);                    
                }                
                // GET - update start position
                currStartPos = tempListIndex[jj];
            }
            // SET
            retObj.Add(iTextLine.Substring(currStartPos));  
            // RET
            return retObj;
        }
        

        【讨论】:

          猜你喜欢
          • 2012-06-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-05-31
          • 1970-01-01
          相关资源
          最近更新 更多