【问题标题】:Split String by length按长度拆分字符串
【发布时间】:2009-06-17 22:31:28
【问题描述】:

我有一个长度为 5000 + 个字符的字符串值,我想将其拆分为 76 个字符长,并在每 76 个字符的末尾添加一个新行。我将如何在 C# 中做到这一点?

【问题讨论】:

  • Aric 说了什么,但是你确定要在一个单词中间分割吗?
  • 写一个电子邮件客户端?
  • 或者您正在处理 UUEncode?​​span>
  • 我使用 base64 编码并将其写入 XML
  • 听起来很正常的理由这样做

标签: c# .net regex split


【解决方案1】:

如果你在写 Base64 数据,试试写

Convert.ToBase64String(bytes, Base64FormattingOptions.InsertLineBreaks);

这将每 76 个字符插入一个换行符

【讨论】:

  • 我会被诅咒的,所以会的。也应该很快,使用不安全的代码。 ;)
  • 很确定这是赢家,因为无论如何其他人都必须通过这个转换器运行
【解决方案2】:

另一方面,如果您希望 StringBuilder 与字符串性能对比,最好的文章是找到的代码项目 here

(但这不显示字符串大小)

简而言之,在字符串长度(或重复的接触)达到阈值之前,StringBuilder 不会更快,因此您完全低于此阈值,因此请坚持常规的字符串连接和 String 方法。

【讨论】:

    【解决方案3】:

    试试这个:

    s = Regex.Replace(s, @"(?<=\G.{76})", "\r\n");
    

    编辑:显然,这是迄今为止发布的所有方法中最慢的方法。我想知道如果您预编译正则表达式会如何:

    Regex rx0 = new Regex(@"(?<=\G.{76})");
    
    s = rx0.Replace(s, "\r\n"); // only time this portion
    

    另外,它与直接匹配方法相比如何?

    Regex rx1 = new Regex(".{76}");
    
    s = rx1.Replace(s, "$0\r\n"); // only time this portion
    

    我一直想知道那些无限制的后视有多昂贵。

    【讨论】:

    • :D One-liners:程序员版的俳句。
    • 艾伦,你的第一个版本没有我最初说的那么慢。乔尔指出我没有重置计时器。您可以在我的博客文章中查看您的时间。我很快就会发布一篇新文章,将每个人的代码重构为方法。
    • 酷。但是你有一个复制/粘贴错误:AlanM_3() 与 AlanM_1() 做的事情完全相同。
    【解决方案4】:

    有点丑……但要快得多;)(这个版本需要 161 个滴答声……Aric 需要 413 个滴答声)

    我在我的博客上发布了我的测试代码。 http://hackersbasement.com/?p=134 (我还发现 StringBuilder 比 string.Join 慢得多)

    http://hackersbasement.com/?p=139

        string chopMe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    
        Stopwatch sw = new Stopwatch();
    
        sw.Start();
        char[] chopMeArray = chopMe.ToCharArray();
        int totalLength = chopMe.Length;
        int partLength = 12;
        int partCount = (totalLength / partLength) + ((totalLength % partLength == 0) ? 0 : 1);
        int posIndex = 0;
        char[] part = new char[partLength];
        string[] parts = new string[partCount];
        int get = partLength;
        for (int i = 0; i < partCount; i++)
        {
            get = Math.Min(partLength, totalLength - posIndex);
            Array.Copy(chopMeArray, posIndex, part, 0, get);
            parts[i] = new string(part, 0, get);
            posIndex += partLength;
        }
    
        var output = string.Join("\r\n", parts) + "\r\n";
        sw.Stop();
        Console.WriteLine(sw.ElapsedTicks);
    

    【讨论】:

    • 如果我的回答你怎么看?
    • 我已经更新了我的答案 - 你可能想再次运行你的基准测试;)
    • 另外,它是“ StopWatch sw = StopWatch.StartNew(); ”
    • 我还没有看到。无论如何,如果我正确地进行基准测试,我的平均运行时间约为 40 次。
    • 我再次更新了我的网站。 (刻度是 Console.WriteLine()s 旁边的 cmets)......乔尔,你是对的,我没有重置计时器。这表明 StringBuilder 版本现在是最快的。
    【解决方案5】:
    public static string InsertNewLine(string s, int len)
    {
        StringBuilder sb = new StringBuilder(s.Length + (int)(s.Length/len) + 1);
        int start = 0;
        for (start=0; start<s.Length-len; start+=len)
        {
            sb.Append(s.Substring(start, len));
            sb.Append(Environment.NewLine);
        }
        sb.Append(s.Substring(start));
        return sb.ToString();
    }
    

    s 是您的输入字符串,len 是所需的行长 (76)。

    【讨论】:

    • +1 是规范的解决方案,完全符合要求。
    • 使用换行符的最佳方式是使用:Environment.NewLine,而不是“\n”。
    【解决方案6】:
    string[] FixedSplit(string s, int len)
    {
       List<string> output;
       while (s.Length > len)
       {
          output.Add(s.Substring(0, len) + "\n");
          s.Remove(0, len);
       }
       output.Add(s + "\n");
       return output.ToArray();
    }
    

    【讨论】:

    • ...如果您想要返回单个字符串,则必须旋转返回的数组并将所有字符串混合在一起。
    • 我该怎么做,因为我确实需要 1 个字符串返回?
    • string.Join("\r\n",输出) + "\r\n"
    • 这也应该可以,但你不需要 \r\n,因为 Aric 已经添加了换行符。
    【解决方案7】:
    public static IEnumerable<string> SplitString(string s, int length)
    {
        var buf = new char[length];
        using (var rdr = new StringReader(s))
        {
            int l;
            l = rdr.ReadBlock(buf, 0, length);
            while (l > 0)
            {
                yield return (new string(buf, 0, l)) + Environment.NewLine;
                l = rdr.ReadBlock(buf, 0, length);
            }
        }
    }
    

    然后将它们重新组合在一起:

    string theString = GetLongString();
    StringBuilder buf = new StringBuilder(theString.Length + theString.Length/76);
    foreach (string s in SplitString(theString, 76) { buf.Append(s); }
    string result = buf.ToString();
    

    或者你可以这样做:

    string InsertNewLines(string s, int interval)
    {
        char[] buf = new char[s.Length + (int)Math.Ceiling(s.Length / (double)interval)];
    
        using (var rdr = new StringReader(s))
        {
            for (int i=0; i<buf.Length-interval; i++)
            {
                rdr.ReadBlock(buf, i, interval);
                i+=interval;
                buf[i] = '\n';
            }
            if (i < s.Length)
            {
                rdr.ReadBlock(buf, i, s.Length - i);
                buf[buf.Length - 1] = '\n';
            }
        }
        return new string(buf);
    }
    

    【讨论】:

    • 这样可以更好地处理大文件。但是速度很慢
    • 主要优点是可以很容易地更改函数以接受普通流。然后,您可以将文件流之类的东西传递给它,并且可能一开始就永远不会将整个字符串加载到内存中。
    • 添加了一种将结果直接放入缓冲区的新方法 - 应该非常快。
    • @Joel,来自 Matthew 的帖子,你说我打错了记号,你是对的,我发现了原因:stackoverflow.com/questions/1017608/…
    【解决方案8】:

    还有一个......(第一次通过缓慢的后续运行,类似于上面发布的更快的时间)

    private void button1_Click(object sender, EventArgs e)
    {
      string chopMe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
      Stopwatch sw = new Stopwatch();
      sw.Start();
      string result = string.Join("\r\n", ChopString(chopMe).ToArray());
      sw.Stop();
      MessageBox.Show(result + " " + sw.ToString());
    }
    
    
    public IEnumerable<string> ChopString(string s)
    {
      int i = 0;
      while (i < s.Length)
      {
        yield return i + PARTLENGTH <= s.Length ? s.Substring(i,PARTLENGTH) :s.Substring(i) ;
        i += PARTLENGTH;
      }
    }
    

    编辑:我很想知道子字符串有多快......

    【讨论】:

    • 你说得很好。对我来说,下一次测试我会将它们全部循环两次。字符串首先解析的方式是最慢的。
    【解决方案9】:

    字符串是 5000 个字符...我不认为速度真的很重要,除非你这样做数千甚至数百万次,尤其是当 OP 甚至没有提到速度很重要时。过早优化?

    我可能会使用递归,因为在我看来,它会导致最简单的代码。

    这可能在语法上不正确,因为我知道 .NET 但不是 C#。

    String ChunkString(String s, Integer chunkLength) {
        if (s.Length <= chunkLength) return s;
        return String.Concat(s.Substring(0, chunkLength), 
                             ChunkString(s.Substring(chunkLength)));
    }
    

    【讨论】:

    • 是的,我们知道;我们只是为了满足我们的好奇心而进行基准测试。
    【解决方案10】:

    主要是为了好玩,这里有一个不同的解决方案,作为字符串的扩展方法实现: (\r\n 明确使用,因此仅支持换行符的格式);

    public static string Split(this string str, int len)
            {
                char org = str.ToCharArray();
                int parts = str.Length / len + (str.Length % len == 0 ? 0 : 1);
                int stepSize = len + newline.Length;
                char[] result = new char[parts * stepSize];
                int resLen = result.Length;
    
                for (int i =0;i<resLen ;i+stepSize)
                {
                    Array.Copy(org,i*len,result,i*stepSize);
                    resLen[i++] = '\r';
                    resLen[i++] = '\n';
                }
                return new string(result);
            }
    

    【讨论】:

      【解决方案11】:

      最后,我想这将是我会使用的

          static string fredou()
          {
              string s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
              int partLength = 12;
      
              int stringLength = s.Length;
              StringBuilder n = new StringBuilder(stringLength + (int)(stringLength / partLength) + 1);
              int chopSize = 0;
              int pos = 0;
      
              while (pos < stringLength)
              {
                  chopSize = (pos + partLength) < stringLength ? partLength : stringLength - pos;
                  n.Append(s , pos, chopSize);
                  n.Append("\r\n");
                  pos += chopSize;
              }
      
              return n.ToString();         
          }
      

      通过查看反射器下的 AppendLine:

          <ComVisible(False)> _
          Public Function AppendLine(ByVal value As String) As StringBuilder
              Me.Append(value)
              Return Me.Append(Environment.NewLine)
          End Function
      
          Public Shared ReadOnly Property NewLine As String
              Get
                  Return ChrW(13) & ChrW(10)
              End Get
          End Property
      

      对我来说,速度明智,手动执行 > AppendLine

      【讨论】:

      • 你赢了最慢的(279097 滴答声)
      • 是的,肯定是正则表达式获胜!
      • 第一个 for 循环在 Fredou 的解决方案中不计时间,因为它构建了测试字符串。它仍然是 270K 滴答吗?我将第一个 for 循环读为 7000 个新字符串初始化。
      • (我真的不认为你想像以前那样做......但我们会看到)
      • 使用 AppendLine() 而不是调用 Append() 两次。此外,由于 .Net 处理字符串的方式,您可能还会看到内存使用量减少。我不得不结束它,但请随时将您的结果作为评论发布在我的博客上。
      【解决方案12】:

      我将字符串分割为 35

      var tempstore ="12345678901234567890123456789012345";
      for (int k = 0; k < tempstore.Length; k += 35)
      {
         PMSIMTRequest.Append(tempstore.Substring(k, tempstore.Length - k > 35 ? 35 : tempstore.Length - k));
         PMSIMTRequest.Append(System.Environment.NewLine);
      }
      messagebox.Show(PMSIMTRequest.tostring());
      

      【讨论】:

        【解决方案13】:

        @M4N 的回答很好,但我觉得while statementfor statement 更容易理解。

        public static string InsertNewLine(string source, int len = 76)
        {
            var sb = new StringBuilder(source.Length + (int)(source.Length / len) + 1);
            var start = 0;
            while ((start + len) < source.Length)
            {
                sb.Append(source.Substring(start, len));
                sb.Append(Environment.NewLine);
                start += len;
            }
            sb.Append(source.Substring(start));
            return sb.ToString();
        }
        

        【讨论】:

          猜你喜欢
          • 2012-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-30
          • 1970-01-01
          • 2022-11-23
          • 2020-08-14
          相关资源
          最近更新 更多