【问题标题】:Shortening a string of numbers缩短一串数字
【发布时间】:2019-11-07 09:45:57
【问题描述】:

我有以下数字序列:

您可以看到很多这些数字。我想缩短那个字符串。假设字符串包含超过 20 个数字,它应该显示 18 个数字,然后是“...”,然后是序列的最后两个。

我可以通过在 List<int>HashSet<int> 中添加这些数字来做到这一点(在这种情况下 HashSet 可能更快),但我认为它会很慢。

StringBuilder temp = new StringBuilder();

for (...)
{
    temp.Append($"{number} ");
}

var sequence = temp.ToString();

我想要的示例:

7 9 12 16 18 21 25 27 30 34 36 39 43 45 48 52 54 57 ... 952 954

请注意,我只想要快速方法

【问题讨论】:

  • 你能把数字序列的声明放到你的问题中吗?很多人(包括我自己)在工作中看不到 imgur 的东西,因为它被屏蔽了......
  • 如果您使用 LINQ,则从您的集合中取出前 18 个元素,然后从您的集合中取出最后 2 个元素并将它们与 .... 连接起来。
  • 这些数字是否在一个字符串中?还是它们已经在某种收藏中?
  • 他在照片后说“字符串”
  • 我只是想确定一下,因为缺少代码

标签: c#


【解决方案1】:

这个版本比其他答案快大约 8 倍,并且只分配大约 6% 的内存。我想你很难找到更快的版本:

static string Truncated(string input)
{
    var indexOfEighteenthSpace = IndexOfCharSeekFromStart(input, ' ', 18);
    if (indexOfEighteenthSpace <= 0) return input;

    var indexOfSecondLastSpace = IndexOfCharSeekFromEnd(input, ' ', 2);
    if (indexOfSecondLastSpace <= 0) return input;

    if (indexOfSecondLastSpace <= indexOfEighteenthSpace) return input;

    var leadingSegment = input.AsSpan().Slice(0, indexOfEighteenthSpace);
    var trailingSegment = input.AsSpan().Slice(indexOfSecondLastSpace + 1);

    return string.Concat(leadingSegment, " ... ", trailingSegment);

    static int IndexOfCharSeekFromStart(string input, char value, int count)
    {
       var startIndex = 0;
       for (var i = 0; i < count; i++)
       {
           startIndex = input.IndexOf(value, startIndex + 1);
           if (startIndex <= 0) return startIndex;
       }

        return startIndex;
    }

    static int IndexOfCharSeekFromEnd(string input, char value, int count)
    {
       var endIndex = input.Length - 1;
       for (var i = 0; i < count; i++)
       {
           endIndex = input.LastIndexOf(value, endIndex - 1);
           if (endIndex <= 0) return endIndex;
       }

        return endIndex;
    }
}   

【讨论】:

  • 什么是 AsSpan?以前没见过
  • @TimRutter 这是 .Net Core 3 的东西。 stevejgordon.co.uk/…
  • @TimRutter: C# Span
  • A Span 代表一个连续的内存块。字符串上的AsSpan() 返回一个ReadOnlySpan&lt;char&gt;,它映射到原始字符串中的底层字符。对 ReadOnlySpan 进行切片让我可以有效地构建子字符串,而无需分配新字符串来保存子字符串,而是只指向初始字符串使用的原始内存。
  • @MatthewWatson 它与 Core 2.1 一起推出,但它不仅在 Core 中可用 - 您还可以在 Framework 和 nuget.org/packages/System.Memory 的朋友中使用它
【解决方案2】:

单独的小步骤

如何从这个序列(字符串)中创建一个列表?

var myList = myOriginalSequence.Split(' ').ToList();

如何从列表中取出前 18 个数字?

var first18Numbers = myList.Take(18);

如何从列表中取出最后 2 个数字?

var last2Numbers = myList.Skip(myList.Count() - 2);

您如何确保仅当列表中的数字超过 20 个时才执行此操作?

if(myList.Count() > 20)

如何从列表中创建一个新的序列字符串?

var myNewSequence = String.Join(" ", myList);

把它们放在一起

var myList = myOriginalSequence.Split(' ').ToList();

string myNewSequence;

if(myList.Count() > 20)
{
    var first18Numbers = myList.Take(18);
    var first18NumbersString = String.Join(" ", first18Numbers);

    var last2Numbers = myList.Skip(myList.Count() - 2);
    var last2NumbersString = String.Join(" ", last2Numbers);

    myNewSequence = $"{first18NumbersString} ... {last2NumbersString}"
}
else
{
    myNewSequence = myOriginalSequence;
}

Console.WriteLine(myNewSequence);

【讨论】:

  • split 刚刚遍历了整个字符串(可能很大)。他要求表现
  • 当只需要前 18 个和后 2 个时,Split 将生成一个包含所有部分的数组。
  • 我不相信拆分整个字符串是最高效的方法。
  • @Flater string.Length 不会遍历字符串的内容。
  • 感谢您的解决方案! @yaakov 的解决方案似乎是最快的
【解决方案3】:

试试这个:

public string Shorten(string str, int startCount, int endCount)
    {
        //first remove any leading or trailing whitespace
        str = str.Trim();

        //find the first startCount numbers by using IndexOf space
        //i.e. this counts the number of spaces from the start until startCount is achieved
        int spaceCount = 1;
        int startInd = str.IndexOf(' ');
        while (spaceCount < startCount && startInd > -1)
        {
            startInd = str.IndexOf(' ',startInd +1);
            spaceCount++;
        }

        //find the last endCount numbers by using LastIndexOf space
        //i.e. this counts the number of spaces from the end until endCount is achieved
        int lastSpaceCount = 1;
        int lastInd = str.LastIndexOf(' ');
        while (lastSpaceCount < endCount && lastInd > -1)
        {
            lastInd =  str.LastIndexOf(' ', lastInd - 1);
            lastSpaceCount++;
        }

        //if the start ind or end ind are -1 or if lastInd <= startIndjust return the str 
        //as its not long enough and so doesn't need shortening
        if (startInd == -1 || lastInd == -1 || lastInd <= startInd) return str;

        //otherwise return the required shortened string
        return $"{str.Substring(0, startInd)} ... {str.Substring(lastInd + 1)}";
    }

这个的输出:

 Console.WriteLine(Shorten("123 123 123 123 123 123 123 123 123 123 123",4,3));

是:

123 123 123 123 ... 123 123 123

【讨论】:

  • 纯代码的答案是不受欢迎的。解释你做了什么,而不是让 OP 复制/粘贴你的答案而不了解问题的实际解决方案是什么。
  • 我添加了一些 cmets
  • @Paul 这不是很有建设性,现在是吗?仅代码的答案对于回答者来说是一个很好的练习,但对于 OP 和其他想要从中学习的后续访问者来说并没有真正的帮助。
  • 嗯,我认为它比没有答案更有用。我同意 cmets(我已经添加了一些)很有帮助,但仅代码的答案并非完全没有帮助。
  • @Paul 我实际上不喜欢 var 用于 int 等简单类型。我只在类型绝对清楚的地方使用它,例如 var rect = new Rectangle();
【解决方案4】:

我认为这可能会有所帮助:

    public IEnumerable<string> ShortenList(string input)
    {
       List<int> list = input.Split(" ").Select(x=>int.Parse(x)).ToList();
        if (list.Count > 20)
        {
            List<string> trimmedStringList = list.Take(18).Select(x=>x.ToString()).ToList();
            trimmedStringList.Add("...");
            trimmedStringList.Add(list[list.Count-2].ToString());
            trimmedStringList.Add(list[list.Count - 1].ToString());

            return trimmedStringList;
        }

        return list.Select(x => x.ToString());
    }

【讨论】:

  • 注意检查应该是&gt; 20,而不是&gt; 19。如果您正好有 20 个项目,则不需要省略号,因为您仍将列出所有数字(前 18 + 最后 2 = 20)
  • 你说的很对,反正你的回答比我的详细多了:-)
【解决方案5】:

不知道这会是什么速度,但作为一个疯狂的建议,你说数字是字符串格式的,看起来它们是用空格分隔的。您可以使用here 找到的任何方法获取第 19 个空格的索引(显示 18 个数字),以及从索引 0 到该索引的子字符串并连接 3 个点。像这样的:

numberListString.SubString(0, IndexOfNth(numberListString, ' ', 19)) + "..."

(可能需要不准确的代码,增加或减少索引或调整值(19)。

编辑:刚刚看到在您想要最后 2 个数字的点之后,您可以使用相同的技术!只需再次连接该结果即可。

注意:我使用这种古怪的技术是因为 OP 说他们想要快速的方法,我只是提供了一个潜在的基准选项:)

【讨论】:

    【解决方案6】:

    有一种替代方法可以防止对整个数字字符串进行迭代,而且速度相当快。

    .NET 中的字符串基本上是一个字符数组,可以使用数组引用 ([1..n]) 单独引用。这可以通过简单地分别从开始和结束测试正确的空格数来利用我们的优势。

    代码中没有任何细节,但可以稍后对其进行优化(例如,通过确保字符串中确实存在某些内容、字符串被修剪等)。

    如果您感觉精力充沛,也可以将以下功能优化为单个功能。

    string finalNumbers = GetStartNumbers(myListOfNumbers, 18);
    if(finalNumbers.EndsWith(" ... "))
        finalNumbers += GetEndNumbers(myListOfNumbers, 2);
    
    
    public string GetStartNumbers(string listOfNumbers, int collectThisManyNumbers) 
    {
        int spaceCounter = 0;    //  The current count of spaces
        int charPointer = 0;     //  The current character in the string
        //  Loop through the list of numbers until we either run out of characters
        //  or get to the appropriate 'space' position...
        while(spaceCounter < collectThisManyNumbers && charPointer <= listOfNumbers.Length)
        {
            //  The following line will add 1 to spaceCounter if the character at the
            //  charPointer position is a space. The charPointer is then incremented...
            spaceCounter += ( listOfNumbers[charPointer++]==' ' ? 1 : 0 );
        }
        //  Now return our value based on the last value of charPointer. Note that
        //  if our string doesn't have the right number of elements, then it will
        //  not be suffixed with ' ... '
        if(spaceCounter < collectThisManyNumbers) 
            return listOfNumbers.Substring(0, charPointer - 1);
        else
            return listOfNumbers.Substring(0, charPointer - 1) + " ... ";
    }
    
    public string GetEndNumbers(string listOfNumbers, int collectThisManyNumbers) 
    {
        int spaceCounter = 0;                       //  The current count of spaces
        int charPointer = listOfNumbers.Length;     //  The current character in the string
        //  Loop through the list of numbers until we either run out of characters
        //  or get to the appropriate 'space' position...
        while(spaceCounter < collectThisManyNumbers && charPointer >= 0)
        {
            //  The following line will add 1 to spaceCounter if the character at the
            //  charPointer position is a space. The charPointer is then decremented...
            spaceCounter += ( listOfNumbers[charPointer--]==' ' ? 1 : 0 );
        }
        //  Now return our value based on the last value of charPointer...
        return listOfNumbers.Substring(charPointer);
    }
    

    有些人认为++-- 的使用令人反感,但这取决于您。如果你想做数学和逻辑,把自己搞砸!

    请注意,这段代码很长,因为它被注释到了屁的远端。

    【讨论】:

      猜你喜欢
      • 2017-11-11
      • 2011-04-28
      • 2019-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-14
      • 2011-11-15
      相关资源
      最近更新 更多