Jaka jest maksymalna możliwa długość łańcucha .NET?


239

Jaki jest najdłuższy ciąg, który można utworzyć w .NET? Dokumenty dla Stringklasy milczą na temat tego pytania, o ile widzę, więc autorytatywna odpowiedź może wymagać pewnej wiedzy na temat elementów wewnętrznych. Czy maksymalna zmiana miałaby miejsce w systemie 64-bitowym?

[To wymaga więcej ciekawości niż praktycznego zastosowania - nie zamierzam tworzyć żadnego kodu, który używa gigantycznych ciągów!]

Odpowiedzi:


346

Teoretyczny limit może wynosić 2 147 483 647, ale praktyczny limit nigdzie nie jest blisko tego. Ponieważ żaden pojedynczy obiekt w programie .NET nie może być większy niż 2 GB, a typ ciągu używa UTF-16 (2 bajty na każdy znak), najlepiej jest 1.007.741.823, ale prawdopodobnie nigdy nie będziesz w stanie przydzielić tego na komputerze 32-bitowym.

Jest to jedna z tych sytuacji, w których „Jeśli musisz zapytać, prawdopodobnie robisz coś złego”.


8
To jest poprawna odpowiedź. Bardziej prawdopodobne jest, że zabraknie pamięci, zanim będziesz w stanie przydzielić wystarczająco dużo, aby wyczerpać długość łańcucha. Przy nowym rozruchu możesz być w stanie pobrać przydział 2 GB (z 1M znaków), jak wspomniano tutaj, ale to wszystko.
Stephen Deken,

4
Zakładając, że twoje twierdzenie „żaden pojedynczy obiekt nie może przekraczać 2 Gb” jest dokładne, jest to zarówno teoretyczny, jak i praktyczny limit - ograniczeniem długości Łańcucha byłby całkowity rozmiar obiektu, a nie pojemność pola Długość.
McKenzieG1

12
Jeśli ktoś jest zainteresowany dokładną wartością, na moim 64-bitowym komputerze jest to 1,073,741,791 (1024 · 1024 · 1024 - 33) znaków. Zobacz także moje powiązane pytanie dotyczące dokładnego maksymalnego rozmiarubyte[] .
sick

4
Szaleję za odpowiedziami, które zawierają krótkie, ale dogłębne wyjaśnienia.
Mikayil Abdullayev

3
Istnieje możliwość zezwalania obiektom .NET 4.5 (i nowszym) na większe niż 2 GB na komputerach 64-bitowych. Sprawdź tutaj
Anderson Matos,

72

Opierając się na moim bardzo naukowym i dokładnym eksperymencie, osiąga szczyt na mojej maszynie na długo przed 1 000 000 000 znaków. (Nadal korzystam z poniższego kodu, aby uzyskać lepsze wskazanie).

AKTUALIZACJA: Po kilku godzinach poddałem się. Ostateczne wyniki: Może przejść o wiele więcej niż 100 000 000 znaków, natychmiast podane System.OutOfMemoryExceptionprzy 1 000 000 000 znaków.

using System;
using System.Collections.Generic;

public class MyClass
{
    public static void Main()
    {
        int i = 100000000;
        try
        {
            for (i = i; i <= int.MaxValue; i += 5000)
            {
                string value = new string('x', i);
                //WL(i);
            }
        }
        catch (Exception exc)
        {
            WL(i);
            WL(exc);
        }
        WL(i);
        RL();
    }

    #region Helper methods

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);   
    }

    private static void RL()
    {
        Console.ReadLine(); 
    }

    private static void Break() 
    {
        System.Diagnostics.Debugger.Break();
    }

    #endregion
}

35
Zastosowanie wyszukiwania binarnego tutaj prawdopodobnie pomoże ci szybciej znaleźć tę odpowiedź ...
Mario,

49

Ponieważ Lengthwłaściwość System.Stringto Int32, zgaduję, że maksymalna długość to 2 147 483 647 znaków (maksymalny Int32rozmiar). Jeśli to pozwoliło dłużej, nie można sprawdzić długości, ponieważ to się nie powiedzie.


2
@ m.edmondson: Właściwie to nie jestem przekonany. Tablica do przypadkach ma LongLengtha a strumień zastosowań longjak długość. Chociaż jest to poprawna odpowiedź, jest to dokładny sposób na zmierzenie tego.
Willem Van Onsem

1
Ale pierwsze dwa bity są używane do wskazania ASCII / nie-ASCII, jak mówi ten artykuł , więc powinno to być 2 ^ 30 = 1 073 741 824
Saito

28

Dla każdego, kto spóźnia się na ten temat, widziałem, że hitcan „prawdopodobnie nie powinieneś tego robić” może sprawić, że ktoś zapyta, co powinien zrobić…

StringBuilder klasy często jest łatwa wymiana. Rozważ jedną z klas opartych na strumieniu, szczególnie jeśli dane pochodzą z pliku.

Problem s += "stuff"polega na tym, że musi on przydzielić całkowicie nowy obszar do przechowywania danych, a następnie skopiować do niego wszystkie stare dane oraz nowe elementy - KAŻDĄ I KAŻDĄ POZYCJĘ PĘTLI. Tak więc dodanie pięciu bajtów do 1 000 000 za pomocą s += "stuff"jest niezwykle kosztowne. Jeśli chcesz po prostu napisać do końca pięć bajtów i kontynuować program, musisz wybrać klasę, która pozostawia trochę miejsca na rozwój:

StringBuilder sb = new StringBuilder(5000);
for (; ; )
    {
        sb.Append("stuff");
    }

StringBuilderwzrośnie automatycznie, podwajając, gdy limit zostanie przekroczony. Tak więc zobaczysz ból wzrostu raz na początku, raz przy 5000 bajtów, ponownie przy 10.000, ponownie przy 20.000. Dołączanie łańcuchów spowoduje ból przy każdej iteracji pętli.


4
Warto również zauważyć, że StringBuilder pozwala ustawić rozmiar początkowy. Przydatne, jeśli wiesz, że z wyprzedzeniem będziesz używać 10 000 000 wpisów, dzięki czemu możesz zignorować niektóre problemy.
Kyle Baran,

3
+1 Za przejrzenie pytania i odpowiedź na dobry projekt. Porównawczo: „to, jak duży może być Twój sznurek, zanim wybuchnie”, w przeciwieństwie do, „jeśli NAPRAWDĘ potrzebujesz przechowywać dużo tekstu, użyj tego ...”
StevoInco

8

Maksymalna długość łańcucha na mojej maszynie to 1 073,741,791 .

Widzisz, ciągi nie są ograniczone liczbą całkowitą, jak się powszechnie uważa.

Pomijając ograniczenia pamięci, ciągi nie mogą mieć więcej niż 2 30 ( 1 073 741 824 ) znaków, ponieważ Microsoft CLR (Common Language Runtime) narzuca ograniczenie 2 GB. 33 więcej niż mój komputer jest dozwolony.

Oto coś, co możesz spróbować samemu.

Utwórz nową aplikację konsoli C # w Visual Studio, a następnie skopiuj / wklej główną metodę tutaj:

static void Main(string[] args)
{
    Console.WriteLine("String test, by Nicholas John Joseph Taylor");

    Console.WriteLine("\nTheoretically, C# should support a string of int.MaxValue, but we run out of memory before then.");

    Console.WriteLine("\nThis is a quickish test to narrow down results to find the max supported length of a string.");

    Console.WriteLine("\nThe test starts ...now:\n");

    int Length = 0;

    string s = "";

    int Increment = 1000000000; // We know that s string with the length of 1000000000 causes an out of memory exception.

    LoopPoint:

    // Make a string appendage the length of the value of Increment

    StringBuilder StringAppendage = new StringBuilder();

    for (int CharacterPosition = 0; CharacterPosition < Increment; CharacterPosition++)
    {
        StringAppendage.Append("0");

    }

    // Repeatedly append string appendage until an out of memory exception is thrown.

    try
    {
        if (Increment > 0)
            while (Length < int.MaxValue)
            {
                Length += Increment;

                s += StringAppendage.ToString(); // Append string appendage the length of the value of Increment

                Console.WriteLine("s.Length = " + s.Length + " at " + DateTime.Now.ToString("dd/MM/yyyy HH:mm"));

            }

    }
    catch (OutOfMemoryException ex) // Note: Any other exception will crash the program.
    {
        Console.WriteLine("\n" + ex.Message + " at " + DateTime.Now.ToString("dd/MM/yyyy HH:mm") + ".");

        Length -= Increment;

        Increment /= 10;

        Console.WriteLine("After decimation, the value of Increment is " + Increment + ".");

    }
    catch (Exception ex2)
    {
        Console.WriteLine("\n" + ex2.Message + " at " + DateTime.Now.ToString("dd/MM/yyyy HH:mm") + ".");

        Console.WriteLine("Press a key to continue...");

        Console.ReadKey();

    }

    if (Increment > 0)
    {
        goto LoopPoint;

    }

    Console.WriteLine("Test complete.");

    Console.WriteLine("\nThe max length of a string is " + s.Length + ".");

    Console.WriteLine("\nPress any key to continue.");

    Console.ReadKey();

}

Moje wyniki były następujące:

Test strunowy autorstwa Nicholasa Johna Josepha Taylora

Teoretycznie C # powinien obsługiwać ciąg int.MaxValue, ale wcześniej zabrakło nam pamięci.

Jest to szybki test zawężający wyniki w celu znalezienia maksymalnej obsługiwanej długości łańcucha.

Test rozpoczyna się ... teraz:

s.Length = 1000000000 at 08/05/2019 12:06

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:06. Po zdziesiątkowaniu wartość Przyrostu wynosi 100000000.

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:06. Po zdziesiątkowaniu wartość Przyrostu wynosi 10000000. s.Length = 1010000000 o 08.05.2019 12:06 s. Długość = 1020000000 o 08.05.2019 12:06 s. Długość = 1030000000 o 08.05.2019 12 : 06 s. Długość = 1040000000 o 08.05.2019 12:06 s. Długość = 1050000000 o 08.05.2019 12:06 s. Długość = 1060000000 o 08.05.2019 12:06 s. Długość = 1070000000 o 08.05.2019 12:06

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:06. Po zdziesiątkowaniu wartość Przyrostu wynosi 1000000. s.Length = 1071000000 o 08.05.2019 12:06 s. Długość = 1072000000 o 08.05.2019 12:06 s. Długość = 1073000000 o 08.05.2019 12 : 06

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:06. Po zdziesiątkowaniu wartość Przyrostu wynosi 100000. s.Length = 1073100000 at 08/05/2019 12:06 s.Length = 1073200000 at 08/05/2019 12:06 s.Length = 1073300000 at 08/05/2019 12 : 06 s. Długość = 1073400000 o 08.05.2019 12:06 s. Długość = 1073500000 o 08.05.2019 12:06 s. Długość = 1073600000 o 08.05.2019 12:06 s. Długość = 1073700000 o 08.05.2019 12:06

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:06. Po zdziesiątkowaniu wartość Przyrostu wynosi 10000. s.Length = 1073710000 at 08/05/2019 12:06 s.Length = 1073720000 at 08/05/2019 12:06 s.Length = 1073730000 at 08/05/2019 12 : 06 s. Długość = 1073740000 o 08.05.2019 12:06

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:06. Po zdziesiątkowaniu wartość Przyrostu wynosi 1000. s.Length = 1073741000 at 08/05/2019 12:06

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:06. Po zdziesiątkowaniu wartość Przyrostu wynosi 100. s.Length = 1073741100 at 08/05/2019 12:06 s.Length = 1073741200 at 08/05/2019 12:06 s.Length = 1073741300 at 08/05/2019 12 : 07 s. Długość = 1073741400 o 08.05.2019 12:07 s. Długość = 1073741500 o 08.05.2019 12:07 s. Długość = 1073741600 o 08.05.2019 12:07 s. Długość = 1073741700 o 08.05.2019 12:07

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:07. Po zdziesiątkowaniu wartość Przyrostu wynosi 10. s.Length = 1073741710 o 08.05.2019 12:07 s.Length = 1073741720 o 08.05.2019 12:07 s.Length = 1073741730 o 08.05.2019 12 : 07 s. Długość = 1073741740 o 08.05.2019 12:07 s. Długość = 1073741750 o 08.05.2019 12:07 s. Długość = 1073741760 o 08.05.2019 12:07 s. Długość = 1073741770 o 08.05.2019 12:07 s. Długość = 1073741780 o 08.05.2019 12:07 s. Długość = 1073741790 o 08.05.2019 12:07

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:07. Po zdziesiątkowaniu wartość Przyrostu wynosi 1. s.Length = 1073741791 na 08.05.2019 12:07

Wyjątek typu "System.OutOfMemoryException 'został wyrzucony. o 08.05.2019 12:07. Po zdziesiątkowaniu wartość przyrostu wynosi 0. Test zakończony.

Maksymalna długość łańcucha wynosi 1073741791.

Naciśnij dowolny klawisz, aby kontynuować.

Maksymalna długość łańcucha na mojej maszynie to 1073741791.

Byłbym bardzo wdzięczny, gdyby ludzie mogli opublikować swoje wyniki jako komentarz poniżej.

Ciekawie będzie się dowiedzieć, czy ludzie uzyskają takie same lub różne wyniki.


„Widzisz, ciągi nie są ograniczone liczbą całkowitą, jak się powszechnie uważa.” -> liczba całkowita w c # może wzrosnąć do 2 147 483 647, a wynik jest bardzo zbliżony (o 32 bajty mniej) do tej wartości podzielonej przez dwa, co jest logiczne, ponieważ każdy znak ciągu jest przechowywany jako Unicode na dwóch bajtach. Więc nawet jeśli limit nie jest narzucony przez liczbę całkowitą, jest on niezwykle zbliżony.
Ben

2

200 megs ... w tym momencie aplikacja zatrzymuje się wirtualnie, ma około gigabajt pamięci operacyjnej, a system operacyjny zaczyna działać tak, jakbyś musiał zrestartować komputer.

static void Main(string[] args)
{
    string s = "hello world";
    for(;;)
    {
        s = s + s.Substring(0, s.Length/10);
        Console.WriteLine(s.Length);
    }
}

12
13
14
15
16
17
18
...
158905664
174796230
192275853
211503438

5
Nie jestem pewien, czy zachowanie, które można uzyskać, tworząc tylko jeden naprawdę duży ciąg, jest takie samo, jak to, co widzisz, przydzielając kilka z nich i łącząc.
Casey

2

Ponieważ String.Lengthjest liczbą całkowitą (to jest alias Int32), jej rozmiar jest ograniczony do Int32.MaxValueznaków Unicode. ;-)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.