string.split () „Out of memory Ausnahme“ beim Lesen durch Tabulatoren getrennte Datei
-
05-07-2019 - |
Frage
Ich bin mit string.split () in meinen C # -Code für Tabulator getrennte Datei zu lesen. Ich bin vor „OutOfMemory Ausnahme“, wie unten in Codebeispiel erwähnt.
Hier würde Ich mag wissen, warum Problem für Datei kommt Größe 16 MB haben?
Das ist richtiger Ansatz oder nicht?
using (StreamReader reader = new StreamReader(_path))
{
//...........Load the first line of the file................
string headerLine = reader.ReadLine();
MeterDataIPValueList objMeterDataList = new MeterDataIPValueList();
string[] seperator = new string[1]; //used to sepreate lines of file
seperator[0] = "\r\n";
//.............Load Records of file into string array and remove all empty lines of file.................
string[] line = reader.ReadToEnd().Split(seperator, StringSplitOptions.RemoveEmptyEntries);
int noOfLines = line.Count();
if (noOfLines == 0)
{
mFileValidationErrors.Append(ConstMsgStrings.headerOnly + Environment.NewLine);
}
//...............If file contains records also with header line..............
else
{
string[] headers = headerLine.Split('\t');
int noOfColumns = headers.Count();
//.........Create table structure.............
objValidateRecordsTable.Columns.Add("SerialNo");
objValidateRecordsTable.Columns.Add("SurveyDate");
objValidateRecordsTable.Columns.Add("Interval");
objValidateRecordsTable.Columns.Add("Status");
objValidateRecordsTable.Columns.Add("Consumption");
//........Fill objValidateRecordsTable table by string array contents ............
int recordNumber; // used for log
#region ..............Fill objValidateRecordsTable.....................
seperator[0] = "\t";
for (int lineNo = 0; lineNo < noOfLines; lineNo++)
{
recordNumber = lineNo + 1;
**string[] recordFields = line[lineNo].Split(seperator, StringSplitOptions.RemoveEmptyEntries);** // Showing me error when we split columns
if (recordFields.Count() == noOfColumns)
{
//Do processing
}
Lösung
Split ist schlecht umgesetzt, und ernsthafte Performance-Problem haben, wenn sie auf große Strings angewendet. Bitte beachten href="http://vpnchoudhary.blogspot.com/2011/03/out-of-memory-exception-simple.html" rel="noreferrer"> dieses Artikels für Details auf die Speicheranforderungen von Split-Funktion :
Was passiert, wenn man eine Spaltung an einer Schnur tut jeden 1355049 Komma getrennt Strings von 16 Zeichen enthält, Gesamtzeichenlänge von 25.745.930 mit?
Ein Array von Zeigern auf String-Objekt: Angrenzend virtueller Adressraum von 4 (Adressenzeiger) * 1.355.049 = 5.420.196 (Arrays Größe) + 16 (für die Buchhaltung) = 5420212.
Nicht zusammenhängender virtueller Adressraum für 1355049 Strings, die jeweils 54 Bytes. Es bedeutet nicht, alle diese 1,3 Millionen Strings alle über den Haufen gestreut werden würde, aber sie werden nicht auf LOH zugeordnet werden. GC wird sie auf den Bündeln auf Gen0 Heap reservieren.
Split.Function wird erstellen interne Array von System.Int32 [] der Größe 25745930, raubend (102.983.736 Bytes) ~ 98MB von LOH, was sehr teuer L ist.
Andere Tipps
Versuchen Sie nicht die ganze Datei in ein Array ersten Lesung "reader.ReadToEnd ()" Lesen Sie die Datei Zeile für Zeile direkt ..
using (StreamReader sr = new StreamReader(this._path))
{
string line = "";
while(( line= sr.ReadLine()) != null)
{
string[] cells = line.Split(new string[] { "\t" }, StringSplitOptions.None);
if (cells.Length > 0)
{
}
}
}
Ich würde Lese line-by-line empfehlen, wenn Sie können, aber manchmal Spaltung durch neue Linien ist nicht die Anforderung.
So können Sie immer Ihre eigenen Speicher effizient Split schreiben. Dies löste das Problem für mich.
private static IEnumerable<string> CustomSplit(string newtext, char splitChar)
{
var result = new List<string>();
var sb = new StringBuilder();
foreach (var c in newtext)
{
if (c == splitChar)
{
if (sb.Length > 0)
{
result.Add(sb.ToString());
sb.Clear();
}
continue;
}
sb.Append(c);
}
if (sb.Length > 0)
{
result.Add(sb.ToString());
}
return result;
}
Ich verwende meine eigenen. Es wird mit 10 Unit-Tests ..
getestetpublic static class StringExtensions
{
// the string.Split() method from .NET tend to run out of memory on 80 Mb strings.
// this has been reported several places online.
// This version is fast and memory efficient and return no empty lines.
public static List<string> LowMemSplit(this string s, string seperator)
{
List<string> list = new List<string>();
int lastPos = 0;
int pos = s.IndexOf(seperator);
while (pos > -1)
{
while(pos == lastPos)
{
lastPos += seperator.Length;
pos = s.IndexOf(seperator, lastPos);
if (pos == -1)
return list;
}
string tmp = s.Substring(lastPos, pos - lastPos);
if(tmp.Trim().Length > 0)
list.Add(tmp);
lastPos = pos + seperator.Length;
pos = s.IndexOf(seperator, lastPos);
}
if (lastPos < s.Length)
{
string tmp = s.Substring(lastPos, s.Length - lastPos);
if (tmp.Trim().Length > 0)
list.Add(tmp);
}
return list;
}
}
Versuchen Sie die Datei linewise statt spalten den ganzen Inhalt zu lesen.