Как я могу получить 3 строки текста из абзаца - PullRequest
0 голосов
/ 05 мая 2010

Я пытаюсь создать «фрагмент» из абзаца. У меня длинный абзац текста со словом посередине. Я хочу получить строку, содержащую слово до этой строки и строку после этой строки.

У меня есть следующая информация:

  • Текст (в строке)
  • Строки ограничены символом NEWLINE \n
  • У меня есть индекс в строке текста, который я хочу подсветить

Пара других критериев:

  • Если мое слово попадает в первую строку абзаца, оно должно показывать первые 3 строки
  • Если мое слово попадает в последнюю строку абзаца, оно должно показывать последние 3 строки
  • Должен показывать весь абзац в вырожденных случаях (абзац содержит только 1 или 2 строки)

Вот пример:

This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph

Например, если мой индекс указывает на BIRD, он должен показывать строки 1, 2 и 3 в виде одной полной строки, например:

This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph

Если мой индекс указывает на СОБАКУ, он должен показывать строки 3, 4 и 5 в виде одной полной строки, например:

This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph

и т.д.

Кто-нибудь хочет помочь решить это?

Ответы [ 4 ]

3 голосов
/ 05 мая 2010

На мой взгляд, это отличная возможность использовать класс StringReader:

  1. Читайте ваш текст построчно.
  2. Храните ваши строки в каком-то буфере (например, Queue<string>), удаляя ненужные строки после прочтения заданного количества строк.
  3. Как только ваша «игла» найдена, прочитайте еще одну строку (если возможно), а затем просто верните то, что находится в вашем буфере.

На мой взгляд, это имеет некоторые преимущества перед другими предлагаемыми подходами:

  1. Поскольку он не использует String.Split, он не выполняет больше работы, чем вам нужно - т.е. читает всю строку, ищет символы для разделения и создает массив подстроки.
  2. На самом деле, вовсе не обязательно читать всю строку вообще , поскольку, как только он находит искомый текст, он заходит настолько далеко, насколько необходимо, чтобы получить желаемое количество строк заполнения.
  3. Он может даже быть реорганизован (очень легко), чтобы иметь возможность обрабатывать любой текстовый ввод с помощью TextReader - например, StreamReader - так что он мог бы работать даже с огромными файлами, без необходимости загружать все содержимое данного файла в память.

Представьте себе такой сценарий: вы хотите найти отрывок текста из текстового файла, который содержит весь текст романа. (Не то, что это ваш сценарий - я просто говорю гипотетически.) Использование String.Split потребует, чтобы весь текст романа был разделен в соответствии с указанным вами разделителем, тогда как StringReader (ну, в этом случае StreamReader) потребуется только чтение до тех пор, пока не будет найден нужный текст, после чего будет возвращен отрывок.

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


Вот быстрая реализация:

// rearranged code to avoid horizontal scrolling
public static string FindSurroundingLines
(string haystack, string needle, int paddingLines) {

    if (string.IsNullOrEmpty(haystack))
        throw new ArgumentException("haystack");
    else if (string.IsNullOrEmpty(needle))
        throw new ArgumentException("needle");
    else if (paddingLines < 0)
        throw new ArgumentOutOfRangeException("paddingLines");

    // buffer needs to accomodate paddingLines on each side
    // plus line containing the needle itself, so:
    // (paddingLines * 2) + 1
    int bufferSize = (paddingLines * 2) + 1;

    var buffer = new Queue<string>(/*capacity*/ bufferSize);

    using (var reader = new StringReader(haystack)) {
        bool needleFound = false;

        while (!needleFound && reader.Peek() != -1) {
            string line = reader.ReadLine();

            if (buffer.Count == bufferSize)
                buffer.Dequeue();

            buffer.Enqueue(line);

            needleFound = line.Contains(needle);
        }

        // at this point either the needle has been found,
        // or we've reached the end of the text (haystack);
        // all that's left to do is make sure the string returned
        // includes the specified number of padding lines
        // on either side
        int endingLinesRead = 0;
        while (
            (reader.Peek() != -1 && endingLinesRead++ < paddingLines) ||
            (buffer.Count < bufferSize)
        ) {
            if (buffer.Count == bufferSize)
                buffer.Dequeue();

            buffer.Enqueue(reader.ReadLine());
        }

        var resultBuilder = new StringBuilder();
        while (buffer.Count > 0)
            resultBuilder.AppendLine(buffer.Dequeue());

        return resultBuilder.ToString();
    }
}

Некоторые примеры ввода / вывода (с text, содержащим ваш пример ввода):

Код:

Console.WriteLine(FindSurroundingLines(text, "MOUSE", 1);

Выход:

This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph

Код:

Console.WriteLine(FindSurroundingLines(text, "BIRD", 1);

Выход:

This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph

Код:

Console.WriteLine(FindSurroundingLines(text, "DOG", 0);

Выход:

This is the 4th line of DOG text in the paragraph

Код:

 Console.WriteLine(FindSurroundingLines(text, "This", 2);

Выход:

This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph
2 голосов
/ 05 мая 2010

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

string[] lines = text.Split('\n');

// Find the right line to work with
int position = 0;
for (int i = 0; i < lines.Count(); i++)
  if (lines[i].Contains(args[0]))
    position = i - 1;

// Get in range if we had a match in the first line
if (position == -1)
  position = 0;

// Adjust the line index so we have 3 lines to work with
if (position > lines.Count() - 3)
  position = lines.Count() - 3;

string result = String.Join("\n", lines.Skip(position).Take(3).ToArray());

Конечно, это можно немного оптимизировать, выйдя из цикла for, как только будет найден индекс, и, возможно, из ряда других вещей. Возможно, вы даже можете LINQify, так что вам больше не нужно хранить этот дополнительный массив, но я не могу придумать, как сделать это прямо сейчас.

Альтернативой для проверки позиции может быть что-то вроде position = Math.Max(0,Math.Min(position, lines.Count() - 3)); - которая будет обрабатывать их обоих одновременно.

1 голос
/ 05 мая 2010

Есть несколько способов справиться с этим:

Первый метод: Используйте String.IndexOf () и String.LastIndexOf () .

Вы можете найти текущее выбранное слово, используя TextBox.SelectionStart () . Затем просто ищите LastIndexOf в месте выбора, ища '\ n', чтобы найти предыдущую строку (не берите первый lastindexof из выбора, как только вы его найдете ... сделайте это снова из этого места, чтобы получить начало этой строки). Затем сделайте то же самое из точки выбора только с помощью IndexOf, чтобы найти '\ n', чтобы получить конец строки. Еще раз, не используйте первый найденный, повторите его, начиная с первого найденного местоположения, чтобы получить конец второй строки. Затем просто поместите текст в найденную область.

Второй метод: Использование String.Split () символом '\ n' (создает массив строк, каждая из которых содержит отдельную строку из текста в порядке индекс массива). Найдите индекс строки, в которой находится текст, а затем просто извлеките из String [index] для строки до, в том числе и после. Надеюсь, что эти два метода достаточно понятны, чтобы вы могли понять, как вы кодируете. Если вы все еще застряли, дайте мне знать.

0 голосов
/ 05 мая 2010

Хорошо. У меня есть трещина,

Я думаю, что первое, что я бы сделал, это разбил все на массивы. Просто потому, что тогда у нас есть простой способ «посчитать» линии.

string[] lines = fullstring.Split('\n');

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

int i = -1;
string animal = 'bird';

foreach(string line in lines)
{
i++;
if(line.indexof(animal) > -1) break;

}
// we will need a if(i == -1) then we didn't find the animal etc

Хорошо, тогда у нас есть очередь. Все, что нам нужно сделать, это ...

if(i == 0)
{
writeln(lines[0);
writeln(lines[1]);
etc
}
else
if(i == lines.count - 1)
{
//this means last array index
}
else
{
//else we are in the middle. So just write out the i -1, i, i+1
}

Я знаю, что это чертовски грязно. Но так я бы решил проблему.

...