Быстро String разбить на StringCollection
От: Nikolay_P_I  
Дата: 14.05.04 10:24
Оценка:
Задача у меня — быстро разбивать околомегобайтные строки на коллекции строк.

Причем разделитель приходит параметром откуда-то из вне моей библиотеки.

Что я вижу:

StrinBuilder.Split — не совсем.

String.Split — не работает со строками, а только с символами, то есть, например,
— по "\r\n" ничего не разделить, так что-б на отдельные '\r' и '\n' не реагировало.

Сейчас работаю с Regex.Split, но проблема в том, что мне может прийти в качестве разделителя нечто вроде "[2345]+", что Regex поймет весьма по-своему.

Попытка написать "в-лоб" комбинацию IndefOf/SubString показала, что выходит медленно.

Подскажите, пожалуйста — как быть ?
Re: Быстро String разбить на StringCollection
От: der Igel Россия  
Дата: 14.05.04 10:34
Оценка: +2
Hello, Nikolay_P_I!

NP> Сейчас работаю с Regex.Split, но проблема в том, что мне может прийти в

NP> качестве разделителя нечто вроде "[2345]+", что Regex поймет
NP> весьма по-своему.

Regex.Escape
Posted via RSDN NNTP Server 1.9 alpha
Re: Быстро String разбить на StringCollection
От: Igor Trofimov  
Дата: 14.05.04 11:00
Оценка:
N_P>Попытка написать "в-лоб" комбинацию IndefOf/SubString показала, что выходит медленно.

Ты в indexof передаешь предыдущее мето, где нашел вхождение или ищешь с начала? Покажи код.
Кстати, а indexof как устроен — он по-тупому ищет или все-таки умеет по Бойеру-Муру?
Re: Быстро String разбить на StringCollection
От: clon Россия  
Дата: 14.05.04 11:01
Оценка: +1
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>Сейчас работаю с Regex.Split, но проблема в том, что мне может прийти в качестве разделителя нечто вроде "[2345]+", что Regex поймет весьма по-своему.

А если эти "весьма" символы обработать "специальным" образом, их ведь не так много..
<< RSDN@Home 1.1.2 stable >>
Нельзя ничего сказать о глубине лужи, пока не попадешь в нее.
Re: Быстро String разбить на StringCollection
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 14.05.04 15:56
Оценка: -1
Здравствуйте, Nikolay_P_I, Вы писали:

Вот тестовый пример. Не оптимизированный под скорость (хотя очень быстрый)

 using System;
using System.Collections.Specialized;
using System.Text;


namespace RSDNRegex
{
 using CSharpTest2;
    /// <summary>
    /// Summary description for Class.
    /// </summary>
    public class CSVHalper
    {

      enum  StateEnum
      {
      FieldStart, ScanField, ScanQuoted, EndQuoted
      }

          public static void ExtractFields(string S,
  ref StringCollection aList)
  {
   ExtractFields(S,ref aList,',','"');
  }
    public static void ExtractFields(string S,
  ref StringCollection aList, char Delimiter, char QuoteChar)


{


 
  //{initialize by clearing the string list, and
  // starting in FieldStart state}
 // Assert(aList <> nil, 'TDExtractFields: list is nil');
  if  (aList == null)
    throw new Exception("?? ???????? ?????? ??? StringCollection ");

  aList.Clear();

  if ( (S==null) || (S.Length ==0 ))
   {
     aList.Add(string.Empty);
    return;
   }

  StateEnum State = StateEnum.FieldStart;
  RStringBuilder SB= new RStringBuilder();
  int StartPos=0;
  int EndPos=S.Length;
  int Inx=0;
 // {read through all the characters in the string}
  while (Inx <EndPos)
  {
 //    {get the next character}
    char Ch = S[Inx];
 //    {switch processing on the state}
    switch (State)
    {
      case StateEnum.FieldStart :

          if  ( Ch == QuoteChar)

              {
                State = StateEnum.ScanQuoted;
                StartPos=Inx+1;
                SB.Length=0;
              }
              else
            if ( Ch == Delimiter)

                aList.Add(string.Empty);

          else
           {
            State = StateEnum.ScanField;
            StartPos=Inx;
            }

          break;

     case StateEnum.ScanField :
        if ( Ch == Delimiter )
           {
     int  CopyCount=Inx-StartPos;
       aList.Add(S.Substring(StartPos,CopyCount));
            State = StateEnum.FieldStart;

          }
          break;
     case StateEnum.ScanQuoted :

          if ( Ch == QuoteChar)
          {
          State = StateEnum.EndQuoted;
          int CopyCount=Inx-StartPos;
          SB.Append(S,StartPos,CopyCount);

          }
          break;
      case StateEnum.EndQuoted :

          if (Ch == Delimiter)
           {
            aList.Add(SB.ToString());
            State = StateEnum.FieldStart;

          }
          else
           if (Ch == QuoteChar)
           {
           State = StateEnum.ScanQuoted;
           SB.Append(QuoteChar);
           StartPos=Inx+1;

           }
          else
           throw new Exception("??? "+Delimiter+ "? ??????? ="+Inx.ToString() );

           break;

    }
    Inx++;
  }
//  {if we are in the ScanQUoted or GotError state at the end
 //  of the string, there was a problem with a closing quote}
  if (State == StateEnum.ScanQuoted)
    throw new Exception("??? ??????????? ?????? ?? ???="+(StartPos-1).ToString()+
     " ?? ????? ??????");
 // {if the current field is not empty, add it to the list}
  if (State == StateEnum.EndQuoted)
       aList.Add(SB.ToString());
       else if (State == StateEnum.ScanField)
        {
       int    CopyCount=Inx-StartPos;
       aList.Add(S.Substring(StartPos,CopyCount));
        }

}

public static string Split(StringCollection aList)
{
 return Split(aList,',',
  '"');

}
public static string Split(StringCollection aList, char Delimiter,
  char QuoteChar)
  {

if  (( (aList==null) || (aList.Count == 0)) ||
    ((aList.Count ==1 ) && (aList[1].Length==0)))
    {
    return  string.Concat(QuoteChar,QuoteChar);

    }

  RStringBuilder Sb= new RStringBuilder();
  //int Pos;

   for ( int j=0; j<aList.Count;j++)
     {
      string S=aList[j];
      int Pos=-1;
      int LenStr=S.Length;

        if (j>0)
         Sb.Append(Delimiter);

       for (int i=0; i< LenStr; i++)
       {
      char Ch=S[i];
        if (( Ch == Delimiter) || (Ch == QuoteChar) || (Ch<33) )
          {
          Sb.Append(QuoteChar);
          Sb.Append(S,0,i);
          Pos=i;
          break;
          }
       }

          if (Pos==-1)
           Sb.Append(S);
            else
            {
              for (int i=Pos; i<LenStr; i++)
             if ( S[i] == QuoteChar)
               {
               Sb.Append(S,Pos,i-Pos+1);
               Sb.Append(QuoteChar);
               Pos=i+1;
               }

               if (Pos<LenStr)
                Sb.Append(S,Pos,LenStr-Pos);

                Sb.Append(QuoteChar);
             }


     }

     return Sb.ToString();
}

    public static void ExtractFields(string S,
  ref StringList aList)
  {
   ExtractFields(S,ref aList,',','"');
  }
    public static void ExtractFields(string S,
  ref StringList aList, char Delimiter, char QuoteChar)


{


 
  //{initialize by clearing the string list, and
  // starting in FieldStart state}
 // Assert(aList <> nil, 'TDExtractFields: list is nil');
  if  (aList == null)
    throw new Exception("?? ???????? ?????? ??? StringCollection ");

  aList.Clear();

  if ( (S==null) || (S.Length ==0 ))
   {
     aList.Add(string.Empty);
    return;
   }

  StateEnum State = StateEnum.FieldStart;
  RStringBuilder SB= new RStringBuilder();
  int StartPos=0;
  int EndPos=S.Length;
  int Inx=0;
 // {read through all the characters in the string}
  while (Inx <EndPos)
  {
 //    {get the next character}
    char Ch = S[Inx];
 //    {switch processing on the state}
    switch (State)
    {
      case StateEnum.FieldStart :

          if  ( Ch == QuoteChar)

              {
                State = StateEnum.ScanQuoted;
                StartPos=Inx+1;
                SB.Length=0;
              }
              else
            if ( Ch == Delimiter)

                aList.Add(string.Empty);

          else
           {
            State = StateEnum.ScanField;
            StartPos=Inx;
            }

          break;

     case StateEnum.ScanField :
        if ( Ch == Delimiter )
           {
     int  CopyCount=Inx-StartPos;
       aList.Add(S.Substring(StartPos,CopyCount));
            State = StateEnum.FieldStart;

          }
          break;
     case StateEnum.ScanQuoted :

          if ( Ch == QuoteChar)
          {
          State = StateEnum.EndQuoted;
          int CopyCount=Inx-StartPos;
          SB.Append(S,StartPos,CopyCount);

          }
          break;
      case StateEnum.EndQuoted :

          if (Ch == Delimiter)
           {
            aList.Add(SB.ToString());
            State = StateEnum.FieldStart;

          }
          else
           if (Ch == QuoteChar)
           {
           State = StateEnum.ScanQuoted;
           SB.Append(QuoteChar);
           StartPos=Inx+1;

           }
          else
           throw new Exception("??? "+Delimiter+ "? ??????? ="+Inx.ToString() );

           break;

    }
    Inx++;
  }
//  {if we are in the ScanQUoted or GotError state at the end
 //  of the string, there was a problem with a closing quote}
  if (State == StateEnum.ScanQuoted)
    throw new Exception("??? ??????????? ?????? ?? ???="+(StartPos-1).ToString()+
     " ?? ????? ??????");
 // {if the current field is not empty, add it to the list}
  if (State == StateEnum.EndQuoted)
       aList.Add(SB.ToString());
       else if (State == StateEnum.ScanField)
        {
       int    CopyCount=Inx-StartPos;
       aList.Add(S.Substring(StartPos,CopyCount));
        }

}

public static string Split(StringList aList)
{
 return Split(aList,',',
  '"');

}
public static string Split(StringList aList, char Delimiter,
  char QuoteChar)
  {

if  (( (aList==null) || (aList.Count == 0)) ||
    ((aList.Count ==1 ) && (aList[1].Length==0)))
    {
    return  string.Concat(QuoteChar,QuoteChar);

    }

  RStringBuilder Sb= new RStringBuilder();
  //int Pos;

   for ( int j=0; j<aList.Count;j++)
     {
      string S=aList[j];
      int Pos=-1;
      int LenStr=S.Length;

        if (j>0)
         Sb.Append(Delimiter);

       for (int i=0; i< LenStr; i++)
       {
      char Ch=S[i];
        if (( Ch == Delimiter) || (Ch == QuoteChar) || (Ch<33) )
          {
          Sb.Append(QuoteChar);
          Sb.Append(S,0,i);
          Pos=i;
          break;
          }
       }

          if (Pos==-1)
           Sb.Append(S);
            else
            {
              for (int i=Pos; i<LenStr; i++)
             if ( S[i] == QuoteChar)
               {
               Sb.Append(S,Pos,i-Pos+1);
               Sb.Append(QuoteChar);
               Pos=i+1;
               }

               if (Pos<LenStr)
                Sb.Append(S,Pos,LenStr-Pos);

                Sb.Append(QuoteChar);
             }


     }

     return Sb.ToString();
}
    }
}
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Re[2]: Быстро String разбить на StringCollection
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 14.05.04 16:05
Оценка:
Здравствуйте, Serginio1, Вы писали:

Если у тебя текст типа Comma Separator Value то этот тестовый класс тебе подойдет. Только замени RStringBuilder на обычный аналог.
типа 2,"10409МОС","10350МОС","""Лучше пузо от пива , чем горб от работы ! ""
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Re[2]: Быстро String разбить на StringCollection
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 14.05.04 16:21
Оценка:
Здравствуйте, Igor Trofimov, Вы писали:

N_P>>Попытка написать "в-лоб" комбинацию IndefOf/SubString показала, что выходит медленно.


iT>Ты в indexof передаешь предыдущее мето, где нашел вхождение или ищешь с начала? Покажи код.

iT>Кстати, а indexof как устроен — он по-тупому ищет или все-таки умеет по Бойеру-Муру?
Да и без Бойеру-Муру в лоб скорость порядка 500 мб/сек. Правда у Бойеру-Муру будет большое премущество на больших объемах при IgnoreCase==true.
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Re: Быстро String разбить на StringCollection
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 14.05.04 16:30
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:

Если тебе нужны все вхождения подстроки воспол таким алгоритмом

 static IntBuilder IgnoreSearchAll(char[] Buf, int BufLen, string Pattern, int PatternLen)
        {
            int CountEquals;
            int LastPos = BufLen - PatternLen + 1;
            int PosInBuf = 0;
            IntBuilder IR=new IntBuilder();
           //    char[] UpperPatern;
            string UpperPatern= Pattern.ToUpper();
            char  FirstChar =UpperPatern[0];
            TextInfo _cultureTextInfo = CultureInfo.CurrentCulture.TextInfo;
        Lab :

            while (PosInBuf < LastPos)
            {
                if (_cultureTextInfo.ToUpper(Buf[PosInBuf]) == FirstChar)
                {
                    CountEquals = 1;
                    while (_cultureTextInfo.ToUpper(Buf[PosInBuf + CountEquals]) == UpperPatern[CountEquals])
                    {
                        CountEquals++;
                        if (CountEquals == PatternLen)
                        {
                            IR.Add(PosInBuf);
                            PosInBuf += PatternLen;
                            goto Lab;
                        }
                    }
                }

                PosInBuf++;
            }

            return IR;
        }
        //======================================================

Ну а одинарный мимвол и так найдешь. Кроме того есть Regex.Matсhes но он достаточно медленный, хотя для мегабайта должен пролетать.
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Re: Быстро String разбить на StringCollection
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.05.04 16:48
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>Сейчас работаю с Regex.Split, но проблема в том, что мне может прийти в качестве разделителя нечто вроде "[2345]+", что Regex поймет весьма по-своему.


В регексах есть функция Escep (или что-то в этом роде). Пропускай свои разделители через нее и будет все пучком.
... << RSDN@Home 1.1.3 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Быстро String разбить на StringCollection
От: IT Россия linq2db.com
Дата: 14.05.04 17:11
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>StrinBuilder.Split — не совсем.


N_P>String.Split — не работает со строками, а только с символами, то есть, например,

N_P>- по "\r\n" ничего не разделить, так что-б на отдельные '\r' и '\n' не реагировало.

string str = "123\r\n456\r789\n346\r\n234";

string[] sa = str.Replace("\r\n", "\0").Split('\0');

foreach (string s in sa)
    Console.WriteLine("---{0}---", s.Replace("\r", "\\r").Replace("\n", "\\n"));
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Быстро String разбить на StringCollection
От: Nikolay_P_I  
Дата: 17.05.04 07:02
Оценка:
Здравствуйте, Все!

Спасибо, Regex.Escape помог.

Но! История получила презабавнейшее продолжение!

Здесь пробегал интересный способ заменить

Regex.Split(stroka, razdelitel)

на

String.Replace('\0', '\b'); //Убираю изначальные 0, они не нужны, но могут быть
String.Replace(razdelitel, "\0")
String.Split('\0');

В чем хохма ?

На малом тесте (5мБ) выигрыш получился в 20(!) раз.
При реальной же работе (100Мб) получаются тормоза в 3(!) раза

Единственное что я заметил — вариант с Regex есть 40-80Мб памяти, вариант с Строками — 60-120Мб. Скорость точно замерить не получилось тк у моего профайлера сбивает крышу на таких
объемах информации.
Re: Быстро String разбить на StringCollection
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 17.05.04 13:09
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>Задача у меня — быстро разбивать околомегобайтные строки на коллекции строк.


N_P>Причем разделитель приходит параметром откуда-то из вне моей библиотеки.


N_P>Что я вижу:


N_P>StrinBuilder.Split — не совсем.


N_P>String.Split — не работает со строками, а только с символами, то есть, например,

N_P>- по "\r\n" ничего не разделить, так что-б на отдельные '\r' и '\n' не реагировало.

public string[] Split(params char[] separator);
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Re[2]: Быстро String разбить на StringCollection
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 17.05.04 13:50
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>public string[] Split(params char[] separator);

Прошу прощения не в ту степь. А та советую тебе взять пример из http://www.rsdn.ru/forum/Message.aspx?mid=641057&amp;only=1
Автор: Serginio1
Дата: 14.05.04

только убрать Upper.

       public class IntBuilder
     {
       private int _count;
      private  int _capacity;


       public int [] IntArray;
       public IntBuilder()
       {
         _capacity=64;
         _count=0;
         IntArray = new int[_capacity];

       }
       public void Add(int value)
       {
       if (_count ==_capacity)
    {
    _capacity*=2;
     int[] TempArray= new int[_capacity];
     Array.Copy(IntArray,0,TempArray,0,_count);
     IntArray=TempArray;
    }
    IntArray[_count]=value;
    _count++;
       }
       public int Count
       {
        get {return _count;}
       }

       public int this[int I]
       {
         get {  return IntArray[I];}
         set {  IntArray[I]= value;}


       }

     }



А обработка строк типа

     int[] ar =IB.IntArray;
     string[] Result new string[IB.Count+1];
     int pos=0;
    int IndexInOrigString=0;
     for ( int i=0; i<IB.Count; i++)
      {
      int PosMetaChar=ar[i];
       if  (PosMetaChar=IndexInOrigString)
         {
         Result[pos]= String.Empty;
         }
         else
        {
        int CopyCount=PosMetaChar-IndexInOrigString;
      Result[pos]= str.SubString(IndexInOrigString,CopyCount)
     
        }

        pos++;
        IndexInOrigString=PosMetaChar+Separator.Length; 
      }

      if  (IndexInOrigString <str.Length)
       Result[pos]= str.SubString(IndexInOrigString,str.Length-IndexInOrigString)


Писал сходу не проверял.
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Re[3]: Быстро String разбить на StringCollection
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.05.04 08:57
Оценка:
Здравствуйте, Serginio1, Вы писали:



Вот наверное правильный вариант

     int[] ar =IB.IntArray;
     string[] Result new string[IB.Count+1];
     int pos=0;
    int IndexInOrigString=0;
     for ( int i=0; i<IB.Count; i++)
      {
      int PosMetaChar=ar[i];
       if  (PosMetaChar=IndexInOrigString)
         {
         Result[pos]= String.Empty;
         }
         else
        {
        int CopyCount=PosMetaChar-IndexInOrigString;
      Result[pos]= str.SubString(IndexInOrigString,CopyCount)
     
        }

        pos++;
        IndexInOrigString=PosMetaChar+Separator.Length; 
      }

      if  (IndexInOrigString <str.Length)
       Result[pos]= str.SubString(IndexInOrigString,str.Length-IndexInOrigString);
         else
         Result[pos]=String.Empty;


Писал сходу не проверял.
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Re[3]: Быстро String разбить на StringCollection
От: VladD2 Российская Империя www.nemerle.org
Дата: 18.05.04 20:44
Оценка: 1 (1)
Здравствуйте, Nikolay_P_I, Вы писали:

N_P>В чем хохма ?


N_P>На малом тесте (5мБ) выигрыш получился в 20(!) раз.

N_P>При реальной же работе (100Мб) получаются тормоза в 3(!) раза

N_P>Единственное что я заметил — вариант с Regex есть 40-80Мб памяти, вариант с Строками — 60-120Мб. Скорость точно замерить не получилось тк у моего профайлера сбивает крышу на таких объемах информации.


Дык в этом и дело. Реплэйс написан оптимальнее чем регексы, но для каждого отдельного реплэйса нужно занимать память. В большом тесте ты вылитаешь из кэша и попадаешь на своп.

Если ты работаешь с действительно большими объемами, то лучше работать с потоками или резать обрабоку на части.
... << RSDN@Home 1.1.3 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.