2012-10-30 9 views
10

Mam bardzo dużą tablicę znaków, którą muszę przekonwertować na ciąg znaków w celu użycia Regex na jej podstawie.
Ale jest tak duży, że dostaję OutOfMemoryException kiedy przekazuję to do konstruktora ciągów.Jak utworzyć ciąg znaków z tablicy znaków bez jej kopiowania?

wiem, że łańcuch jest niezmienne i dlatego nie powinny być możliwe określenie jego podstawowych zbiór znaków, ale muszę się sposób korzystania z wyrażeń regularnych, które bez kopiowania na całość.

Jak uzyskać tę tablicę?

  • Otrzymuję go z pliku przy użyciu StreamReader. Znam pozycję początkową i długość treści do przeczytania, Read i ReadBlock metody potrzebują mnie do dostarczenia bufora char[].

Więc tutaj są rzeczy, które chcę wiedzieć:

  • Czy istnieje sposób, aby określić pod nią kolekcja ciąg jest? (Czy nawet zachowuje znaki w tablicy?)
  • ... lub używając Regex bezpośrednio na tablicy znaków?
  • ... lub uzyskanie części pliku bezpośrednio jako ciąg?
+2

Co to jest wyrażenie regularne? Jeśli jest to dość proste, możesz zastąpić go kodem, który przejdzie przez "char []". –

+0

Jak duży jest "char []"? –

+0

@Matt: Niestety nie mogę. Jest to dość długi i złożony parser. –

Odpowiedz

1

Myślę, że najlepiej byłoby przeczytać wiele fragmentów char [] w pojedyncze ciągi, które nakładają się na pewien wymiar. W ten sposób będziesz mógł wykonać Regex na poszczególnych porcjach, a nakładanie się zapewni ci możliwość zapewnienia, że ​​"przerwa" w porcjach nie złamie wzorca wyszukiwania. W sposób psuedo-kod:

int chunkSize = 100000; 
int overLap = 2000; 

for(int i = 0; i < myCharArray.length; i += chunkSize - overlap) 
{ 
    // Grab your array chunk into a partial string 
    // By having your iteration slightly smaller than 
    // your chunk size you guarantee not to miss any 
    // character groupings. You just need to make sure 
    // your overlap is sufficient to cover the expression 
    string chunk = new String(myCharArray.Skip(i).Take(chunkSize).ToArray()); 
    // run your regex 
} 
+1

Komentarz: Nie ma powodu, aby używać metod Linqa. Pomiń 'i' Take'. Istnieje [przeciążenie konstruktora ciągu] (http://msdn.microsoft.com/en-us/library/ms131424.aspx), aby robić takie rzeczy. Również metoda Linq "ToArray()" skopiuje dane dodatkowy czas. –

+0

+1. Czytanie z nakładającymi się fragmentami wydaje się rozsądnym podejściem do dopasowywania z nieznanym regex w języku C#. Komentarz do komentarza: Myślę, że można używać Skip/Take w próbce, ponieważ dobrze pokazuje zamiar, a to jest oczywiście niewłaściwe, więc nie będzie można go użyć bezpośrednio jako kopiuj-wklej. –

+0

@JeppeStigNielsen: Miał być po prostu kod pseudo-kodowy i zamiast szukać najlepszej metody C#, którą wybrałam z czymś, co wypisywam regularnie, co mnie przekonało. Gdyby ktoś wprowadził powyższy kod dosłownie, zastanawiałbym się nad ich zdrowiem. –

-1

Jeśli używasz .NET 4.0 lub wyższy, co powinno być w użyciu jest MemoryMappedFile. Ta klasa została zaprojektowana wyłącznie w celu manipulowania bardzo dużymi plikami. Z dokumentacji MSDN:

Plik pamięci odwzorowany odwzorowuje zawartość pliku do logicznej przestrzeni adresowej danej aplikacji. Pliki odwzorowane w pamięci umożliwiają programistom pracę z bardzo dużymi plikami, ponieważ pamięć może być zarządzana jednocześnie i umożliwiają pełny, losowy dostęp do pliku bez potrzeby wyszukiwania. Pliki mapowane w pamięci można również udostępniać w wielu procesach .

Po uzyskaniu pliku odwzorowania pamięci, sprawdź, this Stack Overflow answer, w jaki sposób zastosować RegEx do pliku mapowanego w pamięci.

Mam nadzieję, że to pomoże!

+0

-1. Nie wiem, dlaczego użycie MemoryMappedFile byłby lepszy niż StreamReader (trzeba by zająć się kodowaniem ręcznie) ... także niejasne, skąd wziąć dopasowanie C# do kodu Java do uruchamiania Regex na tablicach bajtowych. –

+0

@AlexeiLevenkov - MemoryMappedFile jest lepszy niż StreamReader, ponieważ StreamReader odczytuje cały plik do pamięci. MemoryMappedFile dzieli plik na okna lub widoki, które umożliwiają operowanie na określonym regionie pliku. MemoryMappedFile odczytuje tylko tyle bajtów, ile potrzebujesz, a nie całość. – Icemanind

+4

@icemanind: Źle. StreamReader nie odczytuje całego pliku w pamięci. – SLaks

1

Jedną z raczej brzydkich opcji byłoby użycie niezarządzanej biblioteki RegEx (takiej jak biblioteka wyrażeń regularnych POSIX) i niebezpiecznego kodu. Możesz uzyskać wskaźnik bajtu * do tablicy znaków i przekazać go bezpośrednio do biblioteki niezarządzanej, a następnie przekazać odpowiedzi z powrotem.

fixed (byte * pArray = largeCharArray) 
{ 
    // call unmanaged code with pArray 
} 
1

Jeśli masz znak lub wzór, który można wyszukać, że jest gwarantowana nie być w strukturze starasz się znaleźć, można skanować tablicę dla tego znaku i tworzyć mniejsze ciągi do przetwarzania indywidualnie . Proces byłoby coś jak:

char token = '|'; 
int start = 0; 
int length = 0; 
for(int i = 0; i < charArray.Length; i++;) 
{ 
    if(charArray[i] == token) 
    { 
     string split = new string(charArray,start,length); 
     // check the string using the regex 

     // reset the length 
     length = 0; 
    } 
    else 
    { 
     length++; 
    } 
} 

ten sposób jesteś kopiowania mniejsze segmenty łańcucha, które byłyby GCed po każdej próbie w stosunku do całego łańcucha.

Powiązane problemy