Jeśli zamierzasz wywoływać tę funkcję wiele razy na tym samym długim łańcuchu, ta klasa może być przydatna. Buforuje on nowe pozycje wierszy, aby później mógł wykonać wyszukiwanie O (log (podziały wierszy w łańcuchu)) dla GetLine
i O (1) dla GetOffset
.
public class LineBreakCounter
{
List<int> lineBreaks_ = new List<int>();
int length_;
public LineBreakCounter(string text)
{
if (text == null)
throw new ArgumentNullException(nameof(text));
length_ = text.Length;
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
lineBreaks_.Add(i);
else if (text[i] == '\r' && i < text.Length - 1 && text[i + 1] == '\n')
lineBreaks_.Add(++i);
}
}
public int GetLine(int offset)
{
if (offset < 0 || offset > length_)
throw new ArgumentOutOfRangeException(nameof(offset));
var result = lineBreaks_.BinarySearch(offset);
if (result < 0)
return ~result;
else
return result;
}
public int Lines => lineBreaks_.Count + 1;
public int GetOffset(int line)
{
if (line < 0 || line >= Lines)
throw new ArgumentOutOfRangeException(nameof(line));
if (line == 0)
return 0;
return lineBreaks_[line - 1] + 1;
}
}
Oto mój przypadek testowy:
[TestMethod]
public void LineBreakCounter_ShouldFindLineBreaks()
{
var text = "Hello\nWorld!\r\n";
var counter = new LineBreakCounter(text);
Assert.AreEqual(0, counter.GetLine(0));
Assert.AreEqual(0, counter.GetLine(3));
Assert.AreEqual(0, counter.GetLine(5));
Assert.AreEqual(1, counter.GetLine(6));
Assert.AreEqual(1, counter.GetLine(8));
Assert.AreEqual(1, counter.GetLine(12));
Assert.AreEqual(1, counter.GetLine(13));
Assert.AreEqual(2, counter.GetLine(14));
Assert.AreEqual(3, counter.Lines);
Assert.AreEqual(0, counter.GetOffset(0));
Assert.AreEqual(6, counter.GetOffset(1));
Assert.AreEqual(14, counter.GetOffset(2));
}
Jeśli dzwonisz to dużo, na przykład przez wiele setek/tysięcy linii, są lepsze sposoby niż robisz. Na przykład, jeśli przetwarzasz plik sekwencyjnie, możesz "zapamiętać" numer linii, na którym się znajdujesz, i zwiększać go za każdym razem, gdy uderzysz w znak nowej linii. Możesz też "buforować" numer linii na każde 1000 znaków ze słownikiem i użyć pozycji pamięci podręcznej poprzedzającej zapytanie jako punktu początkowego. Jeśli wydajność nie jest problemem, przejdź bezpośrednio do czegoś czystego Jan/Jona. –
Zobacz ["Czy w tytułach powinny znaleźć się" znaczniki "?] (Http://meta.stackexchange.com/questions/19190/should-questions-include-tags-in-their-titles), gdzie konsensus jest "nie, nie powinni"! –