문제

읽기 탭 분리 파일을 위해 C# 코드에서 String.split ()를 사용하고 있습니다. 코드 샘플에서 아래에 언급 된 것처럼 "OutofMemory Exception"에 직면하고 있습니다.

여기서 16MB 크기의 파일에 문제가 발생하는 이유를 알고 싶습니다.

이것은 올바른 접근법입니까?

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
      }
도움이 되었습니까?

해결책

스플릿은 제대로 구현되며 거대한 문자열에 적용될 때 심각한 성능 문제가 있습니다. 참조하십시오 분할 기능의 메모리 요구 사항에 대한 자세한 내용은이 기사:

총 문자 길이가 25745930 인 1355049 Comma 분리 문자열이 포함 된 문자열에서 분할을하면 어떻게됩니까?

  1. 문자열 대상 배열 : 4의 연속 가상 주소 공간 (주소 포인터)*1355049 = 5420196 (배열 크기) + 16 (책 유지 용) = 5420212.

  2. 1355049 문자열의 비 연속적 인 가상 주소 공간, 54 바이트 각각. 130 만 줄이 힙 전체에 흩어져 있다는 것을 의미하는 것은 아니지만 LOH에 할당되지는 않습니다. GC는 Gen0 힙에 묶음에 할당됩니다.

  3. Split.Function은 System.INT32 [] 크기 25745930의 내부 배열을 생성합니다.

다른 팁

노력하다 ~ 아니다 전체 파일을 배열로 읽으십시오. 먼저 "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;
    }
}

전체 컨텐츠를 분할하는 대신 파일을 라인별로 읽으십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top