#c# #string #split
#c# #строка #разделение
Вопрос:
У меня в памяти огромная строка (несколько сотен мегабайт, я не могу загрузить ее частично), интересно, есть ли какой-нибудь простой способ разделить эту строку на строки, разделенные Environment.NewLine: a) без дублирования этой структуры в памяти (если я использую string.Разделение будет дублировать его) б) с некоторыми методами c # по умолчанию / минимальным количеством эффективного (когда дело доходит до производительности) кода
Практически я хотел бы иметь метод GetNextLineFromString .
Комментарии:
1. Итак, StreamReader? Конечно, для этого требуется поток памяти поверх байтов строки, но почему он вообще хранится в памяти в виде строки?
2. В дополнение к комментарию @Andrewwwilliamson, поскольку исходная строка настолько огромна, вы должны иметь возможность довольно легко проверить
String.Split
использование памяти, используя, скажем, диспетчер задач.3. @CodeCaster ну, я использую какую-то устаревшую dll, которая выдает результат в виде строки
4.
substrings share the same memory as the original string
Я не думаю, что это правда, несмотря на то, что кто-то поддержал ваш комментарий. dotnetfiddle.net/4B6l91 — запустите этот код локально и протестируйте его на себе.5. Мои извинения, я рад, что кто-то это подтвердил, потому что я неправильно запомнил. Я не нашел никакой официальной документации о
Substring
поведении, но ваш ответ демонстрирует это довольно четко, и я нашел предупреждение , которое предполагает, что substring создает копию памяти
Ответ №1:
Вы можете попробовать перечислять строки, например
private static IEnumerable<string> LinesFromString(string value) {
if (string.IsNullOrEmpty(value))
yield break;
int last = 0;
while (true) {
int next = value.IndexOf(Environment.NewLine, last);
if (next < 0) {
yield return value.Substring(last);
yield break;
}
yield return value.Substring(last, next - last);
last = next Environment.NewLine.Length;
}
}
ДЕМОНСТРАЦИЯ:
string text = string.Join(Environment.NewLine,
"ABC",
"D",
"", // <- Empty line
"EF"
);
Console.Write(string.Join(";", LinesFromString(text)));
Результат:
ABC;D;;EF
Если вам нужна N
строка, вы можете выполнить запрос text
с помощью Linq:
int N = 1; // N is zero-based
string line = LinesFromString(text)
.Skip(N - 1)
.FirstOrDefault(); // Either Nth string or null
Редактировать: если вы используете .Net Core 3.1, .Net Standard 2.1 или выше вы можете попробовать представить long string
as ReadOnlyMemory<char>
и работать с его фрагментами; например
private static IEnumerable<ReadOnlyMemory<char>> LinesFromMemory(ReadOnlyMemory<char> value) {
int last = 0;
while (true) {
int at = value.Span.Slice(last).IndexOf(Environment.NewLine);
if (at < 0) {
yield return value.Slice(last);
yield break;
}
yield return value.Slice(last, at);
last = at Environment.NewLine.Length;
}
}
Затем
string huge = string.Join(Environment.NewLine,
"ABC",
"D",
"", // <- Empty line
"EF"
);
// Note, memory is not a string, but some kind of pointer (to huge)
foreach (var memory in LinesFromMemory(huge.AsMemory())) {
// Let's convert memory to string and print it out
string st = memory.ToString();
Console.Write(st);
Console.Write(';');
}
Ответ №2:
Не могли бы вы сохранить строку во временный файл, использовать String.Spilt()
via a StreamReader
, а затем, наконец, удалить файл?
Комментарии:
1. docs.microsoft.com/en-us/dotnet/api /… , вероятно, проще, если вы собираетесь сначала записать его в файл.
2. Конечно, согласен, @mjwills, но моя точка зрения была в основном об освобождении памяти, если это основная проблема.