【问题标题】:Calculating the correct length of string per line with Page X/Y使用页面 X/Y 计算每行字符串的正确长度
【发布时间】:2021-05-02 04:52:08
【问题描述】:

有人问了我一个问题,现在我因为无法得出准确/正确的结果而自责。

假设我们有一个函数可以将一个字符串拆分为多行,但在我们“拆分”到新行之前,每行必须有 x 个字符:

private string[] GetPagedMessages(string input, int maxCharsPerLine) { ... }

对于每一行,我们需要在行尾加入“x/y”,基本上是 1/4、2/4 等... 现在,分页机制也必须是每行长度限制的一部分。

我已经过度劳累、过度思考和绊倒了一些事情,这似乎很简单,但对于我的生活,我无法弄清楚!我没有“得到”什么?

我对什么感兴趣?计算和部分逻辑,但主要是根据每行的最大字符数计算拆分输入需要多少行,这还需要包括 x/y。

请记住:x/y 可以有多个数字(即:不仅是 1/4,还有 10/17 或 99/200)

样品:

input = "这是一条长消息"

maxCharsPerLine = 10

输出:

This i 1/4 // << Max 10 chars
s a lo 2/4 // << Max 10 chars
ng mes 3/4 // << Max 10 chars
sage 4/4 // << Max 10 chars

总体而言,逻辑很简单,但让我失望的只是计算。

【问题讨论】:

  • 如果忽略 x/y 部分的可能长度,我想您必须首先计算出您有多少行。基于此,您可以确定 x/y 部分的长度,然后再次检查这是否会更改总行数。重复直到你有一个稳定的结果。
  • 您使用的是固定宽度字体吗?如果不是,计数字符将不代表任何字符串的实际显示长度。
  • 我不太明白。鉴于 input = "This is a long message"maxCharsPerLine = 10 should be split to 4 lines 和另一个 input = "123456", maxCharsPerLine = 6 为什么后者应该分成 3 行而不仅仅是 1 行?这 3 行来自哪里?它可以拆分为 6 到 1 行之间的任何内容,但 maxCharsPerLine
  • @Fabjan 想到了 SMS 消息。您希望在其限制范围内最大限度地填充消息以尽可能少地发送,但附加一个寻呼机以让接收者知道还有更多消息要发送。因此,您的 6 个字符且限制为 6 个字符的消息会像“12 1/3”、“34 2/3”、“56 3/3”一样发送。是的,我收到的短信实际上是“。(3/3)”,所以它并不像看起来那么简单。
  • @CodeCaster 感谢您的解释。我错过了分页也是消息的一部分,并且 6 个字符的限制也包括它,并且像 '1234561/1' 这样的东西实际上会使它变成 9 个字符长。

标签: c# data-paging


【解决方案1】:

思路:首先,求行数是多少位数:

(n = input.Length, maxCharsPerLine = 10)
if n <= 9*(10-4)  ==> 1 digit
if n <= 9*(10-5) + 90*(10-6)  ==> 2 digits
if n <= 9*(10-6) + 90*(10-7) + 900*(10-8)  ==> 3 digits
if n <= 9*(10-7) + 90*(10-8) + 900*(10-9) + 9000*(10-10)  ==> No solution

然后,减去多余的行数。解决办法:

    private static int GetNumberOfLines(string input, int maxCharsPerLine)
    {
        int n = input.Length;
        int x = maxCharsPerLine;

        for (int i = 4; i < x; i++)
        {
            int j, sum = 0, d = 9, numberOfLines = 0;

            for (j = i; j <= i + i - 4; j++)
            {
                if (x - j <= 0)
                    return -1;   // No solution

                sum += d * (x - j);
                numberOfLines += d;
                d *= 10;
            }
            if (n <= sum)
                return numberOfLines - (sum - n) / (x - j + 1);
        }
        return -2;   // Invalid
    }

用法:

    private static string[] GetPagedMessages(string input, int maxCharsPerLine)
    {
        int numberOfLines = GetNumberOfLines(input, maxCharsPerLine);
        if (numberOfLines < 0)
            return null;

        string[] result = new string[numberOfLines];

        int spaceLeftForLine = maxCharsPerLine - numberOfLines.ToString().Length - 2;  // Remove the chars of " x/y" except the incremental 'x'
        int inputPosition = 0;
        for (int line = 1; line < numberOfLines; line++)
        {
            int charsInLine = spaceLeftForLine - line.ToString().Length;
            result[line - 1] = input.Substring(inputPosition, charsInLine) + $" {line}/{numberOfLines}";
            inputPosition += charsInLine;
        }
        result[numberOfLines-1] = input.Substring(inputPosition) + $" {numberOfLines}/{numberOfLines}";
        return result;
    }

【讨论】:

  • 我喜欢数学,但我发现代码不可读。
  • 同意。代码不可读且未注释。为什么要从4开始?为什么 d = 9 开头?为什么要乘以 10?我不明白。
  • 你是对的。我专注于算法而不是可读性。主要解释在我的第一段。查看第一行:if n &lt;= 9*(10-4) ==&gt; 1 digit 第一行末尾有额外的 4 个字符:“x/y”。这就是“i”以 4 开头的原因。“d”在前 9 行 1-9 中以 9 开​​头。然后 *10 表示 90 行 10-99。然后 *10 表示 900 行 100-999 等。
  • 另外,“知道行数后,拆分成行就很容易了。” - 拆分输入时必须重复部分计算再次,必须再次跟踪行长和寻呼机大小。
  • @CodeCaster 不一定。查看我的编辑以了解使用情况。
【解决方案2】:

一种天真的方法是开始计算行长度减去“寻呼机”的大小,直到行数的大小发生变化(“1/9”短于“1/10”,后者短于“11” /20”等):

private static int[] GetLineLengths(string input, int maxCharsPerLine)
{
    /* The "pager" (x/y) is at least 4 characters (including the preceding space) and at most ... 8?
    * 7/9     (4)
    * 1/10    (5)
    * 42/69   (6)
    * 3/123   (6)
    * 42/420  (7)
    * 999/999 (8)
    */

    int charsRemaining = input.Length;

    var lineLengths = new List<int>();

    // Start with " 1/2", (1 + 1 + 2) = 4 length
    var highestLineNumberLength = 1;

    var lineNumber = 0;
    do
    {
        lineNumber++;

        var currentLineNumberLength = lineNumber.ToString().Length; // 1 = 1, 99 = 2, ...
        if (currentLineNumberLength > highestLineNumberLength)
        {
            // Pager size changed, reset
            highestLineNumberLength = currentLineNumberLength;
            lineLengths.Clear();
            lineNumber = 0;
            charsRemaining = input.Length;
            continue;
        }

        var pagerSize = currentLineNumberLength + highestLineNumberLength + 2;
        var lineLength = maxCharsPerLine - pagerSize;

        if (lineLength <= 0)
        {
            throw new ArgumentException($"Can't split input of size {input.Length} into chunks of size {maxCharsPerLine}");
        }

        lineLengths.Add(lineLength);

        charsRemaining -= lineLength;

    }
    while (charsRemaining > 0);

    return lineLengths.ToArray();
}

用法:

private static string[] GetPagedMessages(string input, int maxCharsPerLine)
{
    if (input.Length <= maxCharsPerLine)
    {
        // Assumption: no pager required for a message that takes one line
        return new[] { input };
    }

    var lineLengths = GetLineLengths(input, maxCharsPerLine);

    var result = new string[lineLengths.Length];

    // Cut the input and append the pager
    var previousIndex = 0;
    for (var i = 0; i < lineLengths.Length; i++)
    {
        var lineLength = Math.Min(lineLengths[i], input.Length - previousIndex); // To cater for final line being shorter
        result[i] = input.Substring(previousIndex, lineLength) + " " + (i + 1) + "/" + lineLengths.Length;
        previousIndex += lineLength;
    }

    return result;
}

打印,例如:

This  1/20
is a  2/20
long  3/20
strin 4/20
g tha 5/20
t wil 6/20
l spa 7/20
n mor 8/20
e tha 9/20
n te 10/20
n li 11/20
nes  12/20
beca 13/20
use  14/20
of i 15/20
ts e 16/20
norm 17/20
ous  18/20
leng 19/20
th 20/20

【讨论】:

  • 当整个输入进入一行时,最后没有打印“1/1”,因此没有正确分割。例如:input = "123456", maxCharsPerLine = 6 应该分成3行,而不是1行。此外,当不存在解决方案时,它会进入无限循环。
  • 第一种情况是在代码中注释的,后一种情况是我发帖后想到的,还没来得及修复。
猜你喜欢
  • 2011-10-13
  • 1970-01-01
  • 2012-11-04
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-21
  • 2015-06-17
相关资源
最近更新 更多