string.split() "استثناء نفاد الذاكرة" عند قراءة ملف مفصول بعلامة التبويب

StackOverflow https://stackoverflow.com/questions/1404435

  •  05-07-2019
  •  | 
  •  

سؤال

أنا أستخدم string.split() في كود C# الخاص بي لقراءة الملف المنفصل بعلامة التبويب.أواجه "استثناء OutOfMemory" كما هو مذكور أدناه في نموذج التعليمات البرمجية.

هنا أود أن أعرف سبب ظهور المشكلة لملف بحجم 16 ميجابايت؟

هل هذا هو النهج الصحيح أم لا؟

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
      }
هل كانت مفيدة؟

المحلول

يتم تنفيذ التقسيم بشكل سيء، ويواجه مشكلة خطيرة في الأداء عند تطبيقه على سلاسل ضخمة.يرجى الرجوع إلى هذه المقالة للحصول على تفاصيل حول متطلبات الذاكرة عن طريق وظيفة الانقسام:

ماذا يحدث عندما تقوم بتقسيم سلسلة تحتوي على 1355049 سلسلة مفصولة بفواصل مكونة من 16 حرفًا لكل منها، ويبلغ إجمالي طول الأحرف 25745930؟

  1. مجموعة من المؤشرات لكائن السلسلة:مساحة عنوان افتراضية متجاورة تبلغ 4 (مؤشر العنوان)*1355049 = 5420196 (حجم المصفوفات) + 16 (لحفظ الدفاتر) = 5420212.

  2. مساحة عنوان ظاهرية غير متجاورة لـ 1355049 سلسلة، كل منها 54 بايت.هذا لا يعني أن كل تلك السلاسل البالغ عددها 1.3 مليون سيتم توزيعها عبر الكومة، لكن لن يتم تخصيصها على LOH.سيقوم GC بتخصيصها على مجموعات في كومة Gen0.

  3. ستقوم Split.Function بإنشاء مصفوفة داخلية من System.Int32[] بحجم 25745930، تستهلك (102983736 بايت) ~98 ميجابايت من LOH، وهو أمر مكلف للغاية.

نصائح أخرى

يحاول لا قراءة الملف بأكمله في مصفوفة أولاً "reader.ReadToEnd()" قراءة الملف سطراً سطراً مباشرة ..

  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)
                {

                }
            }
        }

وأوصي خط سطرا القراءة إذا كنت تستطيع، ولكن تقسيم أحيانا خطوط جديدة ليس الشرط.

وهكذا يمكنك دائما كتابة الذاكرة الخاصة انقسام كفاءة الخاص بك. هذا حل المشكلة بالنسبة لي.

    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;
    }

وأنا استخدم بلدي. وقد تم اختباره مع 10 وحدة الاختبارات ..

public 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;
    }
}

وحاول قراءة linewise الملف بدلا من تقسيم المحتوى كله.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top