【问题标题】:Splitting a string / number every Nth Character / Number?每第 N 个字符/数字拆分一个字符串/数字?
【发布时间】:2011-05-07 05:03:51
【问题描述】:

我需要将一个数字分成偶数部分,例如:

32427237 需要变成 324 272 37
103092501 需要变成 103 092 501

如何拆分它并处理奇数情况,例如导致这些部分的拆分,例如123 456 789 0?

【问题讨论】:

  • 是要将字符串拆分成三个单独的字符串,还是要插入空格?
  • stackoverflow.com/questions/3760152/… 。所有答案都在 java 中,但您可以轻松地将其移植到 c#。
  • 它们是三个单独的字符串

标签: c# .net string split


【解决方案1】:

一个非常简单的方法来做到这一点(不是最有效的,但也不比最有效的慢几个数量级)。

    public static List<string> GetChunks(string value, int chunkSize)
    {
        List<string> triplets = new List<string>();
        while (value.Length > chunkSize)
        {
            triplets.Add(value.Substring(0, chunkSize));
            value = value.Substring(chunkSize);
        }
        if (value != "")
            triplets.Add(value);
        return triplets;
    }

这是一个替代品

    public static List<string> GetChunkss(string value, int chunkSize)
    {
        List<string> triplets = new List<string>();
        for(int i = 0; i < value.Length; i += chunkSize)
            if(i + chunkSize > value.Length)
                triplets.Add(value.Substring(i));
            else
                triplets.Add(value.Substring(i, chunkSize));

        return triplets;
    }

【讨论】:

  • 通用的n 版本会更好。
  • 这两种方法都会产生错误的输出,例如String.Join(", ", GetChunks("20000", 3).ToArray())会产生200, 00,这对于货币是不正确的(应该是20, 000),拆分应该从头开始。另请注意,问题本身要求以这种方式拆分数字,这对于货币拆分也是不正确的
【解决方案2】:

您可以使用一个简单的 for 循环在每个第 n 个位置插入空格:

string input = "12345678";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.Length; i++)
{
    if (i % 3 == 0)
        sb.Append(' ');
    sb.Append(input[i]);
}
string formatted = sb.ToString();

【讨论】:

  • 这与我最初要创建的类似,但有兴趣查看其他替代方案
  • 此解决方案在第一个字符前插入一个空格。
【解决方案3】:

如果您必须在代码中的许多地方这样做,您可以创建一个精美的扩展方法:

static class StringExtensions {

  public static IEnumerable<String> SplitInParts(this String s, Int32 partLength) {
    if (s == null)
      throw new ArgumentNullException(nameof(s));
    if (partLength <= 0)
      throw new ArgumentException("Part length has to be positive.", nameof(partLength));

    for (var i = 0; i < s.Length; i += partLength)
      yield return s.Substring(i, Math.Min(partLength, s.Length - i));
  }

}

然后你可以像这样使用它:

var parts = "32427237".SplitInParts(3);
Console.WriteLine(String.Join(" ", parts));

根据需要输出324 272 37

当您将字符串拆分为多个部分时,即使这些子字符串已经存在于原始字符串中,也会分配新字符串。通常,您不必太在意这些分配,但使用现代 C# 您可以通过稍微改变扩展方法以使用“spans”来避免这种情况:

public static IEnumerable<ReadOnlyMemory<char>> SplitInParts(this String s, Int32 partLength)
{
    if (s == null)
        throw new ArgumentNullException(nameof(s));
    if (partLength <= 0)
        throw new ArgumentException("Part length has to be positive.", nameof(partLength));

    for (var i = 0; i < s.Length; i += partLength)
        yield return s.AsMemory().Slice(i, Math.Min(partLength, s.Length - i));
}

返回类型更改为public static IEnumerable&lt;ReadOnlyMemory&lt;char&gt;&gt;,并通过在未分配的源上调用Slice 创建子字符串。

请注意,如果您在某些时候必须将 ReadOnlyMemory&lt;char&gt; 转换为 string 以在 API 中使用,则必须分配一个新字符串。幸运的是,除了string,还有很多.NET Core API 使用ReadOnlyMemory&lt;char&gt;,所以可以避免分配。

【讨论】:

  • 恕我直言,这里的扩展方法是矫枉过正。要拆分的“数字”是a 特定类型的事物,例如一个订单号。如果您可以将扩展方法限制为仅适用于订单号,那很好。因为你可以将它应用于恰好存储在字符串中的任何实体,这会破坏封装。
  • 确实,我自己想到了一种方法,可以将字符串拆分为任意大小的部分 - 试试这个!
  • 只是一个关于扩展方法的提醒,来自编程指南:“一般来说,我们建议你谨慎地实现扩展方法,并且只在你必须的时候。”抱歉,我只是认为扩展方法被过度使用了。
  • 如果您不想使用扩展方法,请删除 this 关键字并适当地重命名类和方法。我认为这种特殊方法非常通用(而不是特定于说订单号)并且是扩展方法的良好候选者。将类放在单独的命名空间中,开发人员可以决定是否要启用扩展方法,就像添加using System.Linq 会向IEnumerable&lt;T&gt; 添加一堆扩展方法一样。
  • 有人可以告诉我是否有理由使用带有大写字母 S 的字符串?或者我可以正常使用字符串?谢谢
【解决方案4】:

这可能是题外话,因为我不知道你为什么要这样格式化数字,所以如果它不相关,请忽略这篇文章......

整数的显示方式因文化而异。您应该以独立于本地的方式执行此操作,以便以后更容易本地化您的更改。

int.ToString 采用不同的参数,您可以使用它们来格式化不同的文化。 "N" 参数为您提供了文化特定分组的标准格式。

steve x string formatting 也是一个很好的资源。

【讨论】:

  • 它们只是数字,我不遵守任何正常的数字格式,这就是为什么我提到字符串/数字,因为我将把它们用于需要这种特殊格式的东西。
【解决方案5】:

分割方法:

public static IEnumerable<string> SplitInGroups(this string original, int size) {
  var p = 0;
  var l = original.Length;
  while (l - p > size) {
    yield return original.Substring(p, size);
    p += size;
  }
  yield return original.Substring(p);
}

以字符串形式返回,以空格分隔:

var joined = String.Join(" ", myNumber.SplitInGroups(3).ToArray());

编辑:我更喜欢 Martin Liversage 解决方案 :)

编辑 2:修复了一个错误。

编辑 3:添加了将字符串连接回来的代码。

【讨论】:

    【解决方案6】:

    我会做这样的事情,尽管我确信还有其他方法。应该表现不错。

    public static string Format(string number, int batchSize, string separator)
    {      
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i <= number.Length / batchSize; i++)
      {
        if (i > 0) sb.Append(separator);
        int currentIndex = i * batchSize;
        sb.Append(number.Substring(currentIndex, 
                  Math.Min(batchSize, number.Length - currentIndex)));
      }
      return sb.ToString();
    }
    

    【讨论】:

      【解决方案7】:

      我喜欢这个,因为它很酷,虽然不是超级高效:

      var n = 3;
      var split = "12345678900"
                  .Select((c, i) => new { letter = c, group = i / n })
                  .GroupBy(l => l.group, l => l.letter)
                  .Select(g => string.Join("", g))
                  .ToList();
      

      【讨论】:

      • 我自己偏爱 Linq,但效率是一个要求——不过感谢您提供的示例
      【解决方案8】:

      LINQ 规则:

      var input = "1234567890";
      var partSize = 3;
      
      var output = input.ToCharArray()
          .BufferWithCount(partSize)
          .Select(c => new String(c.ToArray()));
      

      更新:

      string input = "1234567890";
      double partSize = 3;
      int k = 0;
      var output = input
          .ToLookup(c => Math.Floor(k++ / partSize))
          .Select(e => new String(e.ToArray()));
      

      【讨论】:

      • System.Linq.EnumerableEx.BufferWithCount 是 .NET (Rx) 的响应式扩展的一部分:msdn.microsoft.com/en-us/devlabs/ee794896.aspx。您必须下载并安装它才能运行代码。
      • 谢谢,我以前没有见过 BufferWithCount - 现在我知道为什么了 - 我已经安装了 Rx,所以可能对其他东西有用。
      【解决方案9】:

      试试这个:

      Regex.Split(num.toString(), "(?<=^(.{8})+)");
      

      【讨论】:

      • 有趣的答案,但你能解释一下正则表达式吗?
      • 我认为这行不通。 (?&lt;=_pattern_) 匹配 pattern 之后的点。 .{8} 匹配 8 个字符。所以这应该匹配字符串开头的 8、16、24、32 等字符。但我发现这种模式重复输出第一组..?
      【解决方案10】:

      如果你知道整个字符串的长度完全可以被部分大小整除,那么使用:

      var whole = "32427237!";
      var partSize = 3;
      var parts = Enumerable.Range(0, whole.Length / partSize)
          .Select(i => whole.Substring(i * partSize, partSize));
      

      但如果整个字符串有可能在末尾有一个小数块,您需要更复杂一点:

      var whole = "32427237";
      var partSize = 3;
      var parts = Enumerable.Range(0, (whole.Length + partSize - 1) / partSize)
          .Select(i => whole.Substring(i * partSize, Math.Min(whole.Length - i * partSize, partSize)));
      

      在这些示例中,部分将是 IEnumerable,但您可以在末尾添加 .ToArray() 或 .ToList() 以防您需要 string[] 或 List 值。

      【讨论】:

      • 如果您认为您正在为已接受的答案增加价值,您应该将自己的答案添加到已回答的问题中。看起来情况并非如此。
      • 我不明白。这是这个问题的第 10 个答案,只有第 3 个可以在单个语句中完成(使用 Linq),我相信这是这 3 个中最有效的。添加这个不同且可能更好的答案我做错了吗?
      【解决方案11】:

      这晚了五年,但是:

      int n = 3;
      string originalString = "32427237";
      string splitString = string.Join(string.Empty,originalString.Select((x, i) => i > 0 && i % n == 0 ? string.Format(" {0}", x) : x.ToString()));
      

      【讨论】:

      • 答案永远不会太晚,我一直想知道是否有 LINQ 解决方案,它就在这里!
      【解决方案12】:

      使用其他 StackOverflow 问题的答案的一个很好的实现:

      "32427237"
          .AsChunks(3)
          .Select(vc => new String(vc))
          .ToCsv(" ");  // "324 272 37"
      
      "103092501"
          .AsChunks(3)
          .Select(vc => new String(vc))
          .ToCsv(" "); // "103 092 501"
      

      AsChunks(): https://stackoverflow.com/a/22452051/538763

      ToCsv(): https://stackoverflow.com/a/45891332/538763

      【讨论】:

        【解决方案13】:

        对于分割字符串并返回每个位置具有特定字符数的字符串列表,这是我的函数:

        public List<string> SplitStringEveryNth(string input, int chunkSize)
            {
                var output = new List<string>();
                var flag = chunkSize;
                var tempString = string.Empty;
                var lenght = input.Length;
                for (var i = 0; i < lenght; i++)
                {
                    if (Int32.Equals(flag, 0))
                    {
                        output.Add(tempString);
                        tempString = string.Empty;
                        flag = chunkSize;
                    }
                    else
                    {
                        tempString += input[i];
                        flag--;
                    }
        
                    if ((input.Length - 1) == i && flag != 0)
                    {
                        tempString += input[i];
                        output.Add(tempString);
                    }
                }
                return output;
            }
        

        【讨论】:

          【解决方案14】:

          您可以使用 Linq 尝试类似的操作。

          var str = "11223344";
          var bucket = 2;    
          var count = (int)Math.Ceiling((double)str.Length / bucket);
                  
          Enumerable.Range(0, count)
              .Select(_ => (_ * bucket))
              .Select(_ => str.Substring(_,  Math.Min(bucket, str.Length - _)))
              .ToList()
          

          【讨论】:

            【解决方案15】:

            您还可以使用 StringReader 类从输入字符串中读取一个字符块,并将字符位置向前计数。

            StringReader Class Read(Char[], Int32, Int32)

            【讨论】:

              【解决方案16】:

              用空格分隔千位的最简单方法是:

              yourString.ToString("#,#").Replace(',', ' ');
              

              【讨论】:

                猜你喜欢
                • 2012-03-17
                • 2022-01-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-11-07
                相关资源
                最近更新 更多