缩短一串数字字符串

3

我有以下数字序列:

enter image description here

你可以看到这些数字很多。 我想缩短这个字符串。 假设该字符串包含超过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

注意,我只希望有快速方法
请注意,本文内容涉及IT技术相关内容。

1
你能把数字序列的声明放到你的问题中吗?很多人(包括我自己)在工作时无法查看imgur的内容,因为它被屏蔽了... - Matthew Watson
如果您正在使用LINQ,则从集合中获取前18个元素,然后获取集合中的最后2个元素,并将它们与....连接起来。 - Jasmin Solanki
这些数字是在一个字符串中吗?还是它们已经在某种集合中了? - d4zed
他在图片后面说:“字符串”。 - Tim Rutter
如果它有21个项目,那么它应该以18 ... 20 21结尾? - CodeCaster
显示剩余3条评论
6个回答

3

小的个体步骤

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

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

如何从列表中获取前18个数字?
var first18Numbers = myList.Take(18);

如何从列表中获取最后两位数?
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);

1
split 刚刚迭代了整个字符串(可能非常大)。他要求性能。 - Tim Rutter
1
Split将生成一个数组,其中包含所有部分,只需要前18个和最后2个。 - xdtTransform
我并不认为将整个字符串拆分是最高效的方法。 - Tim Rutter
@Flater string.Length 不会遍历字符串的内容。 - yaakov
谢谢你提供的解决方案!@yaakov的解决方案似乎是最快的。 - user8591686

3

这个版本比其他答案快大约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?我以前从未见过。 - Tim Rutter
1
@TimRutter 这是 .Net Core 3 的事情。https://www.stevejgordon.co.uk/an-introduction-to-optimising-code-using-span-t - Matthew Watson
1
@TimRutter: C# Span - Flater
1
Span 表示一块连续的内存块。对字符串执行 AsSpan() 方法返回一个 ReadOnlySpan<char>,它映射到原始字符串中的底层字符。对 ReadOnlySpan 进行切片能够有效地构建子字符串,而无需分配新的字符串以容纳子字符串,相反它只是指向最初字符串所使用的原始内存。 - yaakov
1
@MatthewWatson 它是在Core 2.1中推出的,但它不仅适用于Core - 您还可以使用https://www.nuget.org/packages/System.Memory/在Framework和其他相关平台上。 - yaakov
显示剩余3条评论

0

有一种替代方法可以避免迭代整个数字字符串,而且速度相当快。

.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);
}

有些人认为使用++--是不可取的,但这取决于你。如果你想做数学和逻辑方面的事情,就全力以赴吧!

请注意,此代码非常长,因为它被注释到了远处。


0

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

    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());
    }

请注意检查应该是“> 20”,而不是“> 19”。如果您恰好有20个项目,则不需要省略号,因为您仍将列出所有数字(前18个+后2个=总共20个)。 - Flater
你说得完全正确,不管怎样,你的答案比我的详细多了 :-) - XavierAM

0
不知道这个速度会是什么样子,但作为一个大胆的建议,您说数字以字符串格式输入,并且它们似乎是由空格分隔的。您可以使用在此处找到的任何方法获取第19个空格的索引(以显示18个数字),然后从索引0到该索引进行子字符串操作并连接3个点。就像这样:
numberListString.SubString(0, IndexOfNth(numberListString, ' ', 19)) + "..."

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

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

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


0

试试这个:

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));

is:

123 123 123 123 ... 123 123 123


2
仅提供代码的答案是不被赞同的。请解释你所做的事情,而不是让问题的提出者复制/粘贴你的答案,而没有学习实际解决问题的方法。 - Flater
1
我已经添加了一些注释。 - Tim Rutter
2
@Paul,这并不是很有建设性,不是吗?仅仅给出代码的答案对于回答者来说是一个不错的练习,但对于想要从中学习的OP和其他后来的访问者来说并不是很有帮助。 - CodeCaster
我认为这比没有答案更有用。我同意评论(我已经添加了一些)是有帮助的,但仅有代码的答案也不是完全没有帮助。 - Tim Rutter
1
@Paul,对于像int这样的简单类型,我实际上不喜欢使用var。只有在类型绝对清晰的情况下,例如var rect = new Rectangle();,我才会使用它。 - Tim Rutter
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接