utf8 aware strncpy

8

Es fällt mir schwer zu glauben, dass ich die erste Person bin, die auf dieses Problem stößt, aber lange gesucht hat und keine Lösung gefunden hat.

Ich würde gerne strncpy verwenden, aber sollte es UTF8-fähig sein, damit es nicht teilweise ein utf8-Zeichen in die Zielzeichenfolge schreibt.

Andernfalls können Sie nie sicher sein, dass der resultierende String ein gültiges UTF8 ist, auch wenn Sie die Quelle kennen (wenn die Quellzeichenfolge größer als die maximale Länge ist).

Das Überprüfen der resultierenden Zeichenfolge kann funktionieren, aber wenn dies als viel bezeichnet werden soll, ist es besser, eine strncpy-Funktion zu haben, die danach sucht.

glib hat g_utf8_strncpy , aber dies kopiert eine bestimmte Anzahl von Unicode-Zeichen, während Im nach einer Kopierfunktion sucht, die um die Bytelänge begrenzt ist.

Um klar zu sein, durch "utf8 aware" , meine ich, dass es die Grenze des Zielpuffers nicht überschreiten sollte und niemals nur einen Teil einer utf kopieren soll -8 Zeichen. (Eine gültige utf-8-Eingabe darf niemals dazu führen, dass eine ungültige utf-8-Ausgabe ausgegeben wird.)

Hinweis:

Einige Antworten haben darauf hingewiesen, dass strncpy alle Bytes nullt und dass es keine Null-Beendigung gewährleistet. Im Nachhinein hätte ich nach einem utf8 aware strlcpy gefragt, allerdings zu der Zeit, als ich es nicht getan habe kenne die Existenz dieser Funktion nicht.

    
ideasman42 08.09.2011, 07:39
quelle

6 Antworten

1

Um auf die eigene Frage zu antworten, gibt es die C-Funktion, mit der ich endete (C ++ für dieses Projekt nicht verwenden):

Hinweise:  - Erkenne, dass dies kein Klon von strncpy für utf8 ist, sondern eher wie strlcpy von openbsd.  - utf8_skip_data kopiert von glib's gutf8.c  - Es validiert nicht die utf8 - was ich beabsichtigt habe.

Hoffe, das ist nützlich für andere und interessiert an Feedback, aber bitte keine pedantischen Zeloten über NULL Abbruchverhalten, es sei denn, es ist ein tatsächlicher Fehler oder irreführendes / inkorrektes Verhalten.

Danke an James Kanze, der dafür die Basis lieferte, aber unvollständig war und C ++ (ich brauche eine C-Version).

%Vor%     
ideasman42 15.09.2011, 13:58
quelle
6

Ich bin mir nicht sicher, was Sie unter UTF-8 verstehen. strncpy kopiert Bytes, nicht Zeichen, und die Größe des Puffers wird auch in Bytes angegeben. Ob Was du meinst ist, dass es nur vollständige UTF-8-Zeichen kopiert, zum Beispiel, wenn es keinen Platz für den nächsten Charakter gibt, bin ich sich einer solchen Funktion nicht bewusst ist, aber es sollte nicht zu schwer sein zu schreiben:

%Vor%

(Der Inhalt der Tabelle in utf8Size ist ein bisschen schmerzhaft zu erzeugen, aber das ist eine Funktion, die Sie verwenden werden, wenn Sie damit zu tun haben UTF-8, und Sie müssen es nur einmal tun.)

    
James Kanze 08.09.2011 08:05
quelle
6

Ich habe dies an vielen Beispiel-UTF8-Strings mit Multi-Byte-Zeichen getestet. Wenn die Quelle zu lang ist, führt sie eine umgekehrte Suche durch (beginnt am Nullabschlusszeichen) und arbeitet rückwärts, um das letzte vollständige UTF8-Zeichen zu finden, das in den Zielpuffer passen kann. Es stellt immer sicher, dass das Ziel null beendet ist.

%Vor%     
Big Al 08.01.2015 04:05
quelle
1

Hier ist eine C ++ Lösung:

u8string.h :

%Vor%

u8slbcpy.cpp :

%Vor%

Die Funktion u8slbcpy() hat eine C-Schnittstelle, ist aber in C ++ implementiert. Meine Implementierung verwendet die Header-only UTF8-CPP-Bibliothek .

Ich denke, dass dies so ziemlich das, was Sie suchen, aber beachten Sie, dass es immer noch das Problem, dass ein oder mehr Kombinationszeichen kopiert werden möglicherweise nicht, wenn die Kombination von Zeichen auf den gelten n < sup> th Zeichen (selbst kein Kombinationszeichen) und der Zielpuffer ist gerade groß genug, um die UTF-8-Codierung von Zeichen zu speichern 1 bis n , aber nicht die Kombination von Zeichen des Zeichens n . In diesem Fall werden die Bytes, die die Zeichen 1 bis n darstellen, geschrieben, aber keines der kombinierenden Zeichen von n . In der Tat könnte man sagen, dass das n th -Zeichen teilweise geschrieben ist.

    
Daniel Trebbien 08.09.2011 22:09
quelle
1

strncpy() ist eine schreckliche Funktion:

  1. Wenn nicht genügend Speicherplatz vorhanden ist, wird die resultierende Zeichenfolge nicht vollständig beendet .
  2. Wenn genug Platz ist, wird der Rest mit NULs gefüllt. Dies kann schmerzhaft sein, wenn die Zielzeichenfolge sehr groß ist.

Auch wenn die Zeichen im ASCII-Bereich (0x7f und darunter) bleiben, ist die resultierende Zeichenfolge nicht das, was Sie wollen. In dem UTF-8-Fall ist es möglich, dass die Endung und in keiner ungültigen UTF-8-Sequenz endet.

Der beste Rat ist, strncpy() zu vermeiden.

BEARBEITEN: ad 1):

%Vor%

Einverstanden, der Puffer wird nicht überlaufen. Aber das Ergebnis ist immer noch unerwünscht. strncpy () löst nur einen Teil des Problems. Es ist irreführend und unerwünscht.

UPDATE (2012-10-31): Da dies ein unangenehmes Problem ist, habe ich mich entschieden, meine eigene Version zu hacken und das hässliche strncpy () Verhalten nachzuahmen. Der Rückgabewert ist jedoch die Anzahl der kopierten Zeichen.

%Vor%     
wildplasser 08.09.2011 09:46
quelle
0

Um die obige Antwort zu kommentieren, ist "strncpy () eine schreckliche Funktion:". Ich hasse es sogar, solche pauschalen Aussagen zu kommentieren, auf Kosten der Schaffung eines weiteren Internetprogrammierung-Jihad, aber irgendwie wird diese Aussage irreführend für diejenigen sein, die hierher kommen, um nach Antworten zu suchen.

Okay, vielleicht sind C-String-Funktionen "Old School". Vielleicht sollten alle Strings in C / C ++ in irgendeiner Art von intelligenten Containern usw. sein, vielleicht sollte man C ++ anstelle von C verwenden (wenn Sie eine Wahl haben), sind diese eher eine Vorliebe und ein Argument für andere Themen. p>

Ich kam hierher, um nach einem UTF-8 strncpy () zu suchen. Nicht, dass ich es nicht machen könnte (die Codierung ist IMHO einfach und elegant), sondern wollte sehen, wie andere ihre gemacht haben und vielleicht eine in ASM optimierte finden.

Um das "Geschenk der Götter" der Programmierwelt Menschen, legen Sie Ihre Hybris für einen Moment beiseite und schauen Sie sich einige Fakten.

Es gibt nichts Falsches an "strncpy ()", oder irgendeine andere der ähnlichen Funktionen mit den gleichen Nebenwirkungen und Problemen wie "_snprintf ()", etc.

Ich sage: "strncpy () ist nicht schrecklich", sondern "schreckliche Programmierer benutzen es schrecklich".

Was "schrecklich" ist, ist nicht die Regeln zu kennen. Aus Sicherheitsgründen (wie Pufferüberlauf) und Programmstabilitätsimplikationen würde es auch nicht notwendig sein, dass Microsoft beispielsweise "Safe String Functions" zu seiner CRT-Bibliothek hinzufügt, wenn die Regeln genau befolgt werden.

Die wichtigsten:

  1. "sizeof ()" gibt die Länge einer statischen Zeichenfolge mit Terminator zurück.
  2. "strlen ()" gibt die Länge der Zeichenfolge ohne Terminator zurück.
  3. Am meisten, wenn nicht alle "n" Funktionen nur an 'n' klammern, ohne einen Terminator hinzuzufügen.
  4. Es gibt eine implizite Mehrdeutigkeit bezüglich der "Puffergröße" in Funktionen, die Puffergröße benötigen und eingeben. I.E. Die Typen "(char * pszBuffer, int iBufferSize)". Sicherer das Schlimmste anzunehmen und eine Größe eins weniger als die tatsächliche Puffergröße zu überschreiten und am Ende einen Abschlusswiderstand hinzuzufügen.
  5. Legen Sie bei String-Eingaben, Puffern usw. eine angemessene Größenbeschränkung fest, die auf dem erwarteten Durchschnitt und Maximum basiert. Um das Abschneiden von Eingaben hoffentlich zu vermeiden und Pufferüberlaufperioden zu eliminieren.

So handle ich persönlich mit solchen Dingen und anderen Regeln, die nur bekannt und geübt werden sollen.

Ein handliches Makro für die statische Stringgröße:

%Vor%

Beim Deklarieren von lokalen / Stack-String-Puffern:

A) Die Größe ist zum Beispiel auf 1023 + 1 für den Terminator begrenzt, um Strings mit bis zu 1023 Zeichen Länge zu ermöglichen.

B) Ich initialisiere die Zeichenkette auf Null in der Länge, plus endet am Ende, um eine mögliche 'n' Trunkierung abzudecken.

%Vor%

Alternativ könnte man einfach tun: %Code% natürlich, aber dann gibt es eine Performance-Implikation für einen Compiler generiert "memset () wie Aufruf, um den gesamten Puffer auf Null. Es macht Dinge sauberer für das Debuggen, und ich bevorzuge diesen Stil für statische (vs lokale / Stack) Strings Puffer / p>

Nun ein "strncpy ()" nach den Regeln:

%Vor%

Natürlich gibt es andere "Regeln" und Probleme, aber das sind die wichtigsten, die einem einfallen. Sie müssen einfach wissen, wie die lib-Funktionen funktionieren und sichere Methoden wie diese verwenden.

Schließlich benutze ich in meinem Projekt sowieso ICU , also habe ich beschlossen, mitzugehen und die Makros in "utf8" zu verwenden. h "mein eigenes" strncpy () "zu machen.

    
Sirmabus 28.10.2012 15:22
quelle

Tags und Links