Строка для обрезки к длине, игнорирующей HTML

Вы можете получить из Panel и сделать его Selectable и установить для TabStop значение true. Тогда достаточно переопределить ProcessCmdKey и использовать клавиши со стрелками для прокрутки. Не забудьте также установить для его AutoScroll значение true.

Панель выбора - прокручиваемая с клавиатуры

using System.Drawing;
using System.Windows.Forms;
class SelectablePanel : Panel
{
    const int ScrollSmallChange = 10;
    public SelectablePanel()
    {
        SetStyle(ControlStyles.Selectable, true);
        SetStyle(ControlStyles.UserMouse, true);
        TabStop = true;
    }
    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (!Focused)
            return base.ProcessCmdKey(ref msg, keyData);

        var p = AutoScrollPosition;
        switch (keyData)
        {
            case Keys.Left:
                AutoScrollPosition = new Point(-ScrollSmallChange - p.X, -p.Y);
                return true;
            case Keys.Right:
                AutoScrollPosition = new Point(ScrollSmallChange - p.X, -p.Y);
                return true;
            case Keys.Up:
                AutoScrollPosition = new Point(-p.X, -ScrollSmallChange - p.Y);
                return true;
            case Keys.Down:
                AutoScrollPosition = new Point(-p.X, ScrollSmallChange - p.Y);
                return true;
            default:
                return base.ProcessCmdKey(ref msg, keyData);
        }
    }
}
9
задан genesis 22 September 2011 в 15:51
поделиться

5 ответов

Начните с первого символа пост, переступая через каждого персонажа. Каждый раз, когда вы пересекаете персонажа, увеличивайте счетчик. Когда вы найдете символ «<», прекратите увеличивать счетчик, пока не нажмете «>». Ваша позиция, когда счетчик достигает 250, - это то место, где вы действительно хотите отрезать.

10
ответ дан 4 December 2019 в 20:24
поделиться

Если я правильно понимаю проблему, вы хотите сохранить форматирование HTML, но не считать его частью длины строки, которую вы сохраняете.

Вы можете сделать это с помощью кода, который реализует простой конечный автомат .

2 состояния: InTag, OutOfTag
InTag:
- Переходит к OutOfTag, если встречается символ >
- Идет к себе любой другой встреченный персонаж
OutOfTag:
- Переходит к InTag, если встречается символ <
- Идет к себе, встречается любой другой символ

Ваше начальное состояние будет OutOfTag.

Вы реализуете конечный автомат, обрабатывая 1 символ за раз. Обработка каждого персонажа приводит вас в новое состояние.

Когда вы пропускаете текст через конечный автомат, вы также хотите сохранить выходной буфер и длину, с которой он встречался, доступной (чтобы вы знали, когда остановиться).

  1. Увеличивайте переменную Length каждый раз, когда вы находитесь в состоянии OutOfTag и обрабатываете другого персонажа. При желании вы не можете увеличивать эту переменную, если у вас есть символ пробела.
  2. Вы заканчиваете алгоритм, когда у вас больше нет символов или у вас есть желаемая длина, упомянутая в # 1.
  3. В свой выходной буфер включайте символы, с которыми вы сталкиваетесь, вплоть до длины, указанной в # 1.
  4. Храните стопку незакрытых тегов. Когда вы достигнете длины, для каждого элемента в стеке добавьте конечный тег. Проходя по алгоритму, вы можете узнать, когда встретите тег, сохранив переменную current_tag. Эта переменная current_tag запускается при входе в состояние InTag и заканчивается при входе в состояние OutOfTag (или когда встречается символ пробела в состоянии InTag). Если у вас есть стартовый тег, вы кладете его в стек. Если у вас есть конечный тег, вы извлекаете его из стека.
0
ответ дан 4 December 2019 в 20:24
поделиться

Вот реализация, которую я придумал, в C #:

public static string TrimToLength(string input, int length)
{
  if (string.IsNullOrEmpty(input))
    return string.Empty;

  if (input.Length <= length)
    return input;

  bool inTag = false;
  int targetLength = 0;

  for (int i = 0; i < input.Length; i++)
  {
    char c = input[i];

    if (c == '>')
    {
      inTag = false;
      continue;
    }

    if (c == '<')
    {
      inTag = true;
      continue;
    }

    if (inTag || char.IsWhiteSpace(c))
    {
      continue;
    }

    targetLength++;

    if (targetLength == length)
    {
      return ConvertToXhtml(input.Substring(0, i + 1));
    }
  }

  return input;
}

И несколько модульных тестов, которые я использовал через TDD :

[Test]
public void Html_TrimReturnsEmptyStringWhenNullPassed()
{
  Assert.That(Html.TrimToLength(null, 1000), Is.Empty);
}

[Test]
public void Html_TrimReturnsEmptyStringWhenEmptyPassed()
{
  Assert.That(Html.TrimToLength(string.Empty, 1000), Is.Empty);
}

[Test]
public void Html_TrimReturnsUnmodifiedStringWhenSameAsLength()
{
  string source = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
                  "<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
                  "<br/>" +
                  "In an attempt to make a bit more space in the office, kitchen, I";

  Assert.That(Html.TrimToLength(source, 250), Is.EqualTo(source));
}

[Test]
public void Html_TrimWellFormedHtml()
{
  string source = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
             "<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
             "<br/>" +
             "In an attempt to make a bit more space in the office, kitchen, I've pulled out all of the random mugs and put them onto the lunch room table. Unless you feel strongly about the ownership of that Cheyenne Courier mug from 1992 or perhaps that BC Tel Advanced Communications mug from 1997, they will be put in a box and donated to an office in more need of mugs than us. <br/><br/>" +
             "In the meantime we have a nice selection of white Ikea mugs, some random Starbucks mugs, and others that have made their way into the office over the years. Hopefully that will suffice. <br/><br/>" +
             "</div>";

  string expected = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
                    "<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
                    "<br/>" +
                    "In an attempt to make a bit more space in the office, kitchen, I've pulled out all of the random mugs and put them onto the lunch room table. Unless you feel strongly about the ownership of that Cheyenne Courier mug from 1992 or perhaps that BC Tel Advanced Communications mug from 1997, they will be put in";

  Assert.That(Html.TrimToLength(source, 250), Is.EqualTo(expected));
}

[Test]
public void Html_TrimMalformedHtml()
{
  string malformedHtml = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
                         "<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
                         "<br/>" +
                         "In an attempt to make a bit more space in the office, kitchen, I've pulled out all of the random mugs and put them onto the lunch room table. Unless you feel strongly about the ownership of that Cheyenne Courier mug from 1992 or perhaps that BC Tel Advanced Communications mug from 1997, they will be put in a box and donated to an office in more need of mugs than us. <br/><br/>" +
                         "In the meantime we have a nice selection of white Ikea mugs, some random Starbucks mugs, and others that have made their way into the office over the years. Hopefully that will suffice. <br/><br/>";

  string expected = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
              "<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
              "<br/>" +
              "In an attempt to make a bit more space in the office, kitchen, I've pulled out all of the random mugs and put them onto the lunch room table. Unless you feel strongly about the ownership of that Cheyenne Courier mug from 1992 or perhaps that BC Tel Advanced Communications mug from 1997, they will be put in";

  Assert.That(Html.TrimToLength(malformedHtml, 250), Is.EqualTo(expected));
}
0
ответ дан 4 December 2019 в 20:24
поделиться

Разве не самым быстрым способом было бы использовать метод jQuery text () ?

Например:

<ul>
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>

var text = $('ul').text();

Дало бы значение OneTwoThree в текстовой переменной. Это позволит вам получить фактическую длину текста без включенного HTML.

-1
ответ дан 4 December 2019 в 20:24
поделиться

Я знаю, что это немного позже опубликованной даты, но у меня была похожая проблема, и вот как я ее решил. Меня беспокоит скорость регулярного выражения по сравнению с взаимодействием через массив.

Также, если у вас есть пробел перед тегом html, и после этого это не исправляет

private string HtmlTrimmer(string input, int len)
{
    if (string.IsNullOrEmpty(input))
        return string.Empty;
    if (input.Length <= len)
        return input;

    // this is necissary because regex "^"  applies to the start of the string, not where you tell it to start from
    string inputCopy;
    string tag;

    string result = "";
    int strLen = 0;
    int strMarker = 0;
    int inputLength = input.Length;     

    Stack stack = new Stack(10);
    Regex text = new Regex("^[^<&]+");                
    Regex singleUseTag = new Regex("^<[^>]*?/>");            
    Regex specChar = new Regex("^&[^;]*?;");
    Regex htmlTag = new Regex("^<.*?>");

    while (strLen < len)
    {
        inputCopy = input.Substring(strMarker);
        //If the marker is at the end of the string OR 
        //the sum of the remaining characters and those analyzed is less then the maxlength
        if (strMarker >= inputLength || (inputLength - strMarker) + strLen < len)
            break;

        //Match regular text
        result += text.Match(inputCopy,0,len-strLen);
        strLen += result.Length - strMarker;
        strMarker = result.Length;

        inputCopy = input.Substring(strMarker);
        if (singleUseTag.IsMatch(inputCopy))
            result += singleUseTag.Match(inputCopy);
        else if (specChar.IsMatch(inputCopy))
        {
            //think of &nbsp; as 1 character instead of 5
            result += specChar.Match(inputCopy);
            ++strLen;
        }
        else if (htmlTag.IsMatch(inputCopy))
        {
            tag = htmlTag.Match(inputCopy).ToString();
            //This only works if this is valid Markup...
            if(tag[1]=='/')         //Closing tag
                stack.Pop();
            else                    //not a closing tag
                stack.Push(tag);
            result += tag;
        }
        else    //Bad syntax
            result += input[strMarker];

        strMarker = result.Length;
    }

    while (stack.Count > 0)
    {
        tag = stack.Pop().ToString();
        result += tag.Insert(1, "/");
    }
    if (strLen == len)
        result += "...";
    return result;
}
0
ответ дан 4 December 2019 в 20:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: