2009-06-16 4 views
3

Ist das möglich? Da C# unveränderliche Strings verwendet, könnte man erwarten, dass es ein Verfahren sein, entlang der Linien von:Zeichenpuffer zwischen C# -String-Objekten teilen

var expensive = ReadHugeStringFromAFile(); 
var cheap = expensive.SharedSubstring(1); 

Wenn es keine solche Funktion, warum die Mühe mit Strings unveränderlich zu machen? Oder alternativ, wenn Strings bereits aus anderen Gründen unveränderbar sind, warum bieten Sie diese Methode nicht an?

Der spezifische Grund, den ich in diesem untersuchen werde, tut etwas Parsing. Einfache rekursive Abstiegs-Parser (wie der von TinyPG erzeugte oder leicht von Hand geschriebene) verwenden Teilstring überall. Das bedeutet, wenn Sie ihnen eine große Datei zum Parsen geben, ist die Speicherabwanderung unglaublich. Sicher gibt es Workarounds - im Grunde rollen Sie Ihre eigene SubString-Klasse, und dann vergessen Sie natürlich, String-Methoden wie StartsWith oder String-Bibliotheken wie Regex zu verwenden, so dass Sie auch Ihre eigene Version dieser rollen müssen. Ich nehme an, dass Parser-Generatoren wie ANTLR das grundsätzlich tun, aber mein Format ist einfach genug, um ein solches Monster-Tool nicht zu rechtfertigen. Selbst TinyPG ist wahrscheinlich ein Overkill.

jemand bitte sagen Sie mir, ich bin einige offensichtliche oder nicht so offensichtliche Standard C# Methodenaufruf irgendwo fehlt ...

+0

Ich habe gerade eine mögliche Problemumgehung getestet. Eine Möglichkeit, das Problem zu lösen, wäre, einen Regex mit der Mitte eines Strings abgleichen zu können. Dies ist möglich, wenn Sie "^. {N}" an den Anfang der Regex anhängen. Es scheint jedoch, dass die Regex-Bibliothek nicht schlau genug ist, um N Zeichen in einem Vorgang zu überspringen. Es dauert O (N) Zeit, so dass Übereinstimmungen länger und länger dauern, wenn N wächst. Seufzer. –

+0

Hah, natürlich hat Regex.Match einen optionalen startat-Parameter, der offensichtlich O (1) ist. Die Workaround funktioniert also doch. Ich würde es keine saubere Lösung nennen, aber es wird ... –

Antwort

5

Nein, nichts ist wie es ist.

. NET-Zeichenfolgen enthalten ihre Textdaten direkt, im Gegensatz zu Java-Zeichenfolgen, die einen Verweis auf ein char-Array haben, einen Versatz und eine Länge.

Beide Lösungen haben in einigen Situationen "Gewinne" und in anderen Fällen Verluste.

Wenn Sie absolut sicher sind das wird ein Mörder für Sie sein, könnten Sie eine Java-Stil-Zeichenfolge für den Einsatz in Ihren eigenen internen APIs implementieren.

+0

Das war, wovor ich Angst hatte. Seufzer. Danke ... –

+0

Ah, ich habe mich gerade heute darüber gewundert. –

1

Das .NET-Framework unterstützt string interning. Dies ist eine Teillösung, bietet jedoch nicht die Möglichkeit, Teile eines Strings wiederzuverwenden. Ich denke, die Wiederverwendung von Substring wird einige Probleme verursachen, die auf den ersten Blick nicht offensichtlich sind. Wenn Sie eine Menge String-Manipulation mit dem StringBuilder tun müssen, ist der Weg zu gehen.

+0

String Interning (in Java und C#) ist hirntot, da Sie vor dem Aufruf von Intern ein String-Objekt erstellen müssen. Für etwas mit dem erklärten Zweck, die Speichernutzung zu speichern, würden Sie erwarten, dass Sie in der Lage wären, ihm einen StringBuilder oder ein (Stück) -Array oder ähnliches zu übergeben. Aber nein, "das wäre zu einfach". –

+0

Das stimmt ... aber der GC ...: D –

+0

Da ich einen Regex nicht auf den (Präfix von) einem StringBuilder anwenden kann, ohne einen String zu konstruieren (und damit die Zeichen zu kopieren), stößt man immer noch auf das Problem Kopieren von 5G Text beim Analysieren einer 1M-Datei. Ich könnte nur sagen, dass keines meiner Token länger als X-Zeichen ist und wiederholt Präfix-Teilzeichenfolgen aus dem StringBuilder erzeugt, die einfach funktionieren können ... einfach wird es nicht sein. Seufzer. –

2

Soweit ich weiß, verwenden alle größeren Parser Streams von zu analysieren. Passt das nicht zu Ihrer Situation?

+0

Nein, weil ich einen Regex nicht auf einen Stream anwenden kann, oder einfach nur einen zerstörungsfreien Test mache, ob er mit einer literalen Zeichenfolge beginnt. Um also einen Stream direkt zu parsen, muss (zumindest) die gesamte Komplexität eines Regex neu implementiert werden, was ANTLR tut, weshalb ich es als "Monster" im Vergleich zu etwas Trivialem wie TinyPG bezeichnete. TinyPG ruft Substring jedoch wiederholt auf, um den Rest der Eingabe abzurufen. Geben Sie ihr eine 1MB-Datei, die auf 100.000 Token aufgeteilt ist, und Sie kopieren 5 GB Arbeitsspeicher. Huch! –

0

Nichts in C# bietet Ihnen die Funktionalität, die Sie suchen.

Was wollen ist ein Rope data structure, eine unveränderliche Datenstruktur, die O (1) Konkat und O (Log n) Teilstrings unterstützt. Ich kann keine C# Implementierungen eines Seils finden, aber here a Java one.

Abgesehen davon ist nichts falsch daran, TinyPG oder ANTLR zu verwenden, wenn dies der einfachste Weg ist, Dinge zu erledigen.

0

Nun, Sie könnten "unsichere" verwenden, um die Speicherverwaltung selbst durchzuführen, was Ihnen ermöglichen könnte, das zu tun, wonach Sie suchen. Auch die StringBuilder-Klasse eignet sich hervorragend für Situationen, in denen eine Zeichenfolge mehrmals manipuliert werden muss, da bei jeder Manipulation keine neue Zeichenfolge erstellt wird.

+0

Ich würde gerne tun, außer dass Sie eine Regex nicht auf einen StringBuffer anwenden können :-( –

0

Sie könnten leicht eine triviale Klasse schreiben, um "billig" darzustellen. Es würde nur den Index des Anfangs des Teilstrings und die Länge des Teilstrings enthalten.Ein paar Methoden Sie erlauben würde, die Teilkette aus, wenn nötig zu lesen - ein String Cast-Operator ideal sein würde, wie Sie

string text = myCheapObject; 

verwenden könnten und es würde funktionieren nahtlos, als ob es eine tatsächliche Zeichenfolge war. Hinzufügen von Unterstützung für ein paar praktische Methoden wie StartsWith wäre schnell und einfach (sie würden alle ein Liner sein). Die andere Option besteht darin, einen regulären Parser zu schreiben und Ihre Token in einem Dictionary zu speichern, von dem Sie Referenzen auf die Token freigeben, anstatt mehrere Kopien zu behalten.

+0

Wenn Sie eine CheapString-Klasse erstellen würde es nicht mit Regex funktionieren – albertein

+0

Ja, das Problem ist, dass Zeichenfolge versiegelt ist, so können Sie Wenn Sie eine 500-MB-Datei laden und parsen müssen, erhalten Sie 1 GB (UTF8 to wchar), was beim Aufteilen auf 2 GB geht. In meinem Fall erscheinen die Elemente aus Reihenfolge und die Datei möglicherweise in einem langsamen Netzwerk, so dass selbst wenn Sie es umwandeln kann, wird die Leistung schrecklich. – russbishop

+0

@xenadu, können Sie eine Streaming-Implementierung schreiben und immer noch große I/O-Puffer verwenden, um die Leistung gut zu halten Haben Sie eine Reihe von const Tokens in der Datei, dann sollten Sie nicht in Tausende von kleinen Strings, sondern nur Tausende von Referenzen (oder Indizes) zu Strings in einem kleinen Wörterbuch zerhacken, ähnlich wenn Sie eine Zahl haben, die als String dargestellt wird "123765.6567853567896" dann Wenn Sie es in einen Gleitkommawert konvertieren, sparen Sie 36 Byte oder mehr. Normalerweise gibt es viele Dinge, die Sie tun können, um Geschwindigkeit und Speichereffizienz zu verbessern. Wenn das schief geht, besorge dir einen 64 Bit PC und Chuck 256GB Ram oder sogar nur eine SSD hinein. –

Verwandte Themen