Si va a llamar a la función muchas veces en la misma cadena larga, esta clase puede ser útil. Almacena en caché las nuevas posiciones de línea, para luego poder realizar O (log (saltos de línea en cadena)) búsqueda GetLine
y O (1) para 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;
}
}
Aquí es mi caso de prueba:
[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));
}
Si se llama a esto mucho, por ejemplo, durante muchos cientos/miles de líneas, hay mejores maneras de lo que está haciendo. Por ejemplo, si está procesando un archivo secuencialmente, podría 'recordar' el número de línea en el que se encuentra e incrementarlo cada vez que toque una nueva línea. O bien, podría "almacenar en caché" el número de línea por cada 1000 caracteres más o menos con un diccionario, y usar la entrada de caché que precede a la consulta como punto de partida. Si el rendimiento no es un problema, busque algo claro directamente de Jan/Jon. –
Consulte ["¿Las preguntas deberían incluir" etiquetas "en sus títulos?]] (Http://meta.stackexchange.com/questions/19190/should-questions-include-tags-in-their-titles), donde el consenso es "no, no deberían"! –