Wie können Sie ungültige Hexadezimalzeichen aus einer XML-basierten Datenquelle entfernen, bevor Sie einen XmlReader oder ein XPathDocument erstellen, das die Daten verwendet?


76

Gibt es eine einfache / allgemeine Möglichkeit, eine XML-basierte Datenquelle vor der Verwendung in einem XmlReader zu bereinigen, damit ich XML-Daten ordnungsgemäß verwenden kann, die nicht den für XML geltenden hexadezimalen Zeichenbeschränkungen entsprechen?

Hinweis:

  • Die Lösung muss XML-Datenquellen verarbeiten, die andere Zeichencodierungen als UTF-8 verwenden, z. B. indem die Zeichencodierung in der XML-Dokumentdeklaration angegeben wird. Es war ein wichtiger Knackpunkt, die Zeichenkodierung der Quelle nicht zu entstellen, während ungültige hexadezimale Zeichen entfernt wurden.
  • Das Entfernen ungültiger hexadezimaler Zeichen sollte nur hexadezimal codierte Werte entfernen, da Sie häufig href-Werte in Daten finden können, die zufällig eine Zeichenfolge enthalten, die einer Zeichenfolgenübereinstimmung für ein hexadezimales Zeichen entspricht.

Hintergrund:

Ich muss eine XML-basierte Datenquelle verwenden, die einem bestimmten Format entspricht (z. B. Atom- oder RSS-Feeds), möchte jedoch veröffentlichte Datenquellen verwenden können, die ungültige Hexadezimalzeichen gemäß der XML-Spezifikation enthalten.

Wenn Sie in .NET einen Stream haben, der die XML-Datenquelle darstellt, und dann versuchen, ihn mit einem XmlReader und / oder XPathDocument zu analysieren, wird eine Ausnahme ausgelöst, da ungültige Hexadezimalzeichen in die XML-Daten aufgenommen werden. Mein aktueller Versuch, dieses Problem zu beheben, besteht darin, den Stream als Zeichenfolge zu analysieren und einen regulären Ausdruck zu verwenden, um die ungültigen hexadezimalen Zeichen zu entfernen und / oder zu ersetzen. Ich suche jedoch nach einer leistungsfähigeren Lösung.

Antworten:


76

Es ist vielleicht nicht perfekt (Hervorhebung hinzugefügt, da die Leute diesen Haftungsausschluss vermissen), aber was ich in diesem Fall getan habe, ist unten. Sie können die Verwendung mit einem Stream anpassen.

/// <summary>
/// Removes control characters and other non-UTF-8 characters
/// </summary>
/// <param name="inString">The string to process</param>
/// <returns>A string with no control characters or entities above 0x00FD</returns>
public static string RemoveTroublesomeCharacters(string inString)
{
    if (inString == null) return null;

    StringBuilder newString = new StringBuilder();
    char ch;

    for (int i = 0; i < inString.Length; i++)
    {

        ch = inString[i];
        // remove any characters outside the valid UTF-8 range as well as all control characters
        // except tabs and new lines
        //if ((ch < 0x00FD && ch > 0x001F) || ch == '\t' || ch == '\n' || ch == '\r')
        //if using .NET version prior to 4, use above logic
        if (XmlConvert.IsXmlChar(ch)) //this method is new in .NET 4
        {
            newString.Append(ch);
        }
    }
    return newString.ToString();

}

1
Probieren Sie die unten stehende Lösung von dnewcome aus.
Eugene Katz

2
-1 Diese Antwort ist irreführend, da sie Zeichen entfernt, die in XML gültig sind, keine Steuerzeichen sind und die in UTF-8 gültig sind.
Daniel Cassidy

2
Wenn Sie die Antwort mit einer besseren Auswahl an Filtern aktualisieren möchten, können Sie dies gerne tun. Wie meine Antwort besagt, ist es vielleicht nicht perfekt, aber es hat meinen Bedürfnissen entsprochen.
Eugene Katz

3
Ich habe XmlConvert.IsXmlChar (ch) für meinen Filter verwendet.
Brad J

1
@BradJ, sehr guter Punkt. Die Methode scheint in .NET 4 hinzugefügt worden zu sein, also wurde der Code so umgeschaltet, dass er nur im Beispiel verwendet wird. Vielen Dank!
Eugene Katz

60

Ich mag Eugenes Whitelist-Konzept. Ich musste etwas Ähnliches tun wie das Originalposter, aber ich musste alle Unicode-Zeichen unterstützen, nicht nur bis zu 0x00FD. Die XML-Spezifikation lautet:

Char = # x9 | #xA | #xD | [# x20- # xD7FF] | [# xE000- # xFFFD] | [# x10000- # x10FFFF]

In .NET beträgt die interne Darstellung von Unicode-Zeichen nur 16 Bit, daher können wir 0x10000-0x10FFFF nicht explizit zulassen. Die XML-Spezifikation verhindert ausdrücklich , dass die Ersatzcodepunkte ab 0xD800 angezeigt werden. Es ist jedoch möglich, dass, wenn wir diese Ersatzcodepunkte in unserer Whitelist zulassen, die utf-8-Codierung unserer Zeichenfolge am Ende gültiges XML erzeugt, solange die richtige utf-8-Codierung aus den Ersatzpaaren von utf-16-Zeichen in der Liste erstellt wurde .NET-Zeichenfolge. Ich habe dies jedoch nicht untersucht, also habe ich mich für die sicherere Wette entschieden und die Leihmütter in meiner Whitelist nicht zugelassen.

Die Kommentare in Eugenes Lösung sind jedoch irreführend. Das Problem ist, dass die Zeichen, die wir ausschließen, in XML nicht gültig sind. Sie sind perfekt gültige Unicode-Codepunkte. Wir entfernen keine "Nicht-Utf-8-Zeichen". Wir entfernen utf-8-Zeichen, die möglicherweise nicht in wohlgeformten XML-Dokumenten enthalten sind.

public static string XmlCharacterWhitelist( string in_string ) {
    if( in_string == null ) return null;

    StringBuilder sbOutput = new StringBuilder();
    char ch;

    for( int i = 0; i < in_string.Length; i++ ) {
        ch = in_string[i];
        if( ( ch >= 0x0020 && ch <= 0xD7FF ) || 
            ( ch >= 0xE000 && ch <= 0xFFFD ) ||
            ch == 0x0009 ||
            ch == 0x000A || 
            ch == 0x000D ) {
            sbOutput.Append( ch );
        }
    }
    return sbOutput.ToString();
}

es wird angehängt & und dies führt doc = XDocument.Load(@strXMLPath);zu einer Ausnahme
CODError

1
Hallo, denkst du, XmlConvert.IsXmlChar () wäre genauer? Eugenes Antwort hat sich seit Ihrem letzten Kommentar geändert. danke
DaFi4

30

Um ungültige XML-Zeichen zu entfernen, empfehle ich die Verwendung der XmlConvert.IsXmlChar- Methode. Es wurde seit .NET Framework 4 hinzugefügt und wird auch in Silverlight dargestellt. Hier ist das kleine Beispiel:

void Main() {
    string content = "\v\f\0";
    Console.WriteLine(IsValidXmlString(content)); // False

    content = RemoveInvalidXmlChars(content);
    Console.WriteLine(IsValidXmlString(content)); // True
}

static string RemoveInvalidXmlChars(string text) {
    char[] validXmlChars = text.Where(ch => XmlConvert.IsXmlChar(ch)).ToArray();
    return new string(validXmlChars);
}

static bool IsValidXmlString(string text) {
    try {
        XmlConvert.VerifyXmlChars(text);
        return true;
    } catch {
        return false;
    }
}

13

DRY-Implementierung der Lösung dieser Antwort (unter Verwendung eines anderen Konstruktors - Sie können auch den in Ihrer Anwendung benötigten verwenden):

public class InvalidXmlCharacterReplacingStreamReader : StreamReader
{
    private readonly char _replacementCharacter;

    public InvalidXmlCharacterReplacingStreamReader(string fileName, char replacementCharacter) : base(fileName)
    {
        this._replacementCharacter = replacementCharacter;
    }

    public override int Peek()
    {
        int ch = base.Peek();
        if (ch != -1 && IsInvalidChar(ch))
        {
            return this._replacementCharacter;
        }
        return ch;
    }

    public override int Read()
    {
        int ch = base.Read();
        if (ch != -1 && IsInvalidChar(ch))
        {
            return this._replacementCharacter;
        }
        return ch;
    }

    public override int Read(char[] buffer, int index, int count)
    {
        int readCount = base.Read(buffer, index, count);
        for (int i = index; i < readCount + index; i++)
        {
            char ch = buffer[i];
            if (IsInvalidChar(ch))
            {
                buffer[i] = this._replacementCharacter;
            }
        }
        return readCount;
    }

    private static bool IsInvalidChar(int ch)
    {
        return (ch < 0x0020 || ch > 0xD7FF) &&
               (ch < 0xE000 || ch > 0xFFFD) &&
                ch != 0x0009 &&
                ch != 0x000A &&
                ch != 0x000D;
    }
}

Vielleicht ist es besser, XmlConvert.IsXmlChar () über die ch-Bereichsprüfungen hinweg zu verwenden? Was denken Sie?
DaFi4

@montewhizdoh: IsXmlChar ist neu in .NET 4. Wenn dies für Sie verfügbar ist, können Sie es gerne verwenden. Diese Lösung ist .NET 2.0+.
Neolisk

1
Der gleiche Ansatz, den ich für mich selbst implementiert habe, aber von Stream geerbt habe, was keine so gute Idee war, da Stream.Read () mit dem Array von Bytes und nicht mit Zeichen arbeitete und es nicht so elegant war, die Zeichen zu überprüfen. Ihre Lösung durch das Erben von StreamReader ist besser, danke!
Mar

1
+1 Da dies das Lesen von WIRKLICH großen XML-Dateien ermöglicht (erfolgreich mit 100 MB-Dateien getestet). Lösungen, die alles in einen String geladen haben, bevor die fehlerhaften Zeichen herausgefiltert wurden, sind mit OutOfMemory-Ausnahmen fehlgeschlagen.
Brad Oestreicher

9

Wenn Sie die Antwort von dnewcombe modernisieren , können Sie einen etwas einfacheren Ansatz wählen

public static string RemoveInvalidXmlChars(string input)
{
    var isValid = new Predicate<char>(value =>
        (value >= 0x0020 && value <= 0xD7FF) ||
        (value >= 0xE000 && value <= 0xFFFD) ||
        value == 0x0009 ||
        value == 0x000A ||
        value == 0x000D);

    return new string(Array.FindAll(input.ToCharArray(), isValid));
}

oder mit Linq

public static string RemoveInvalidXmlChars(string input)
{
    return new string(input.Where(value =>
        (value >= 0x0020 && value <= 0xD7FF) ||
        (value >= 0xE000 && value <= 0xFFFD) ||
        value == 0x0009 ||
        value == 0x000A ||
        value == 0x000D).ToArray());
}

Mich würde interessieren, wie die Leistung dieser Methoden verglichen wird und wie sie alle mit einem Black-List-Ansatz verglichen werden Buffer.BlockCopy.


Ich hatte ein Problem mit der Linq-Methode, die System.OutOfMemoryException auslöst, wenn die XML-Zeichenfolge für größere XML-Dateien verwendet wird.
Brad J

@BradJ vermutlich ist die übergebene Zeichenfolge in diesen Fällen sehr lang?
Jodrell

@BradJ Letztendlich wäre eine Art Stream-Transformation besser, Sie könnten dies direkt an übergeben, XmlReader.Createanstatt die gesamte Datei in eine Zeichenfolge im Speicher zu laden.
Jodrell

2
Ich habe gerade einen Geschwindigkeitstest im Vergleich zur Antwort von dnewcombe durchgeführt und beide Lösungen sind ungefähr 3-4 mal schneller, wobei die Linq-Version nur geringfügig langsamer ist als Ihre Nicht-Linq-Version. Ich hatte diesen Unterschied nicht erwartet. verwendete lange Zeichenfolgen und 100.000 Iterationen mit Stoppuhr, um das Timing zu ermitteln.
Seher

@Seer Ich verwende Zeichenströme mit einer Länge von ~ 60.000 und diese Lösung ist etwas langsamer als die StringBuilder-Methode. Ich bin mir nicht sicher, was ich anders gemacht habe.
Adotout

5

Hier ist dnewcome ‚s Antwort in einem benutzerdefinierten Stream. Es umschließt einfach einen echten Stream-Reader und ersetzt die gelesenen Zeichen.

Ich habe nur wenige Methoden implementiert, um mir Zeit zu sparen. Ich habe dies in Verbindung mit XDocument.Load und einem Dateistream verwendet und nur die Read-Methode (char [] buffer, int index, int count) wurde aufgerufen, also hat es so funktioniert. Möglicherweise müssen Sie zusätzliche Methoden implementieren, damit dies für Ihre Anwendung funktioniert. Ich habe diesen Ansatz verwendet, weil er effizienter zu sein scheint als die anderen Antworten. Ich habe auch nur einen der Konstruktoren implementiert. Sie können natürlich jeden der StreamReader-Konstruktoren implementieren, die Sie benötigen, da es sich nur um einen Durchgang handelt.

Ich habe mich dafür entschieden, die Zeichen zu ersetzen, anstatt sie zu entfernen, da dies die Lösung erheblich vereinfacht. Auf diese Weise bleibt die Länge des Textes gleich, sodass kein separater Index nachverfolgt werden muss.

public class InvalidXmlCharacterReplacingStreamReader : TextReader
{
    private StreamReader implementingStreamReader;
    private char replacementCharacter;

    public InvalidXmlCharacterReplacingStreamReader(Stream stream, char replacementCharacter)
    {
        implementingStreamReader = new StreamReader(stream);
        this.replacementCharacter = replacementCharacter;
    }

    public override void Close()
    {
        implementingStreamReader.Close();
    }

    public override ObjRef CreateObjRef(Type requestedType)
    {
        return implementingStreamReader.CreateObjRef(requestedType);
    }

    public void Dispose()
    {
        implementingStreamReader.Dispose();
    }

    public override bool Equals(object obj)
    {
        return implementingStreamReader.Equals(obj);
    }

    public override int GetHashCode()
    {
        return implementingStreamReader.GetHashCode();
    }

    public override object InitializeLifetimeService()
    {
        return implementingStreamReader.InitializeLifetimeService();
    }

    public override int Peek()
    {
        int ch = implementingStreamReader.Peek();
        if (ch != -1)
        {
            if (
                (ch < 0x0020 || ch > 0xD7FF) &&
                (ch < 0xE000 || ch > 0xFFFD) &&
                ch != 0x0009 &&
                ch != 0x000A &&
                ch != 0x000D
                )
            {
                return replacementCharacter;
            }
        }
        return ch;
    }

    public override int Read()
    {
        int ch = implementingStreamReader.Read();
        if (ch != -1)
        {
            if (
                (ch < 0x0020 || ch > 0xD7FF) &&
                (ch < 0xE000 || ch > 0xFFFD) &&
                ch != 0x0009 &&
                ch != 0x000A &&
                ch != 0x000D
                )
            {
                return replacementCharacter;
            }
        }
        return ch;
    }

    public override int Read(char[] buffer, int index, int count)
    {
        int readCount = implementingStreamReader.Read(buffer, index, count);
        for (int i = index; i < readCount+index; i++)
        {
            char ch = buffer[i];
            if (
                (ch < 0x0020 || ch > 0xD7FF) &&
                (ch < 0xE000 || ch > 0xFFFD) &&
                ch != 0x0009 &&
                ch != 0x000A &&
                ch != 0x000D
                )
            {
                buffer[i] = replacementCharacter;
            }
        }
        return readCount;
    }

    public override Task<int> ReadAsync(char[] buffer, int index, int count)
    {
        throw new NotImplementedException();
    }

    public override int ReadBlock(char[] buffer, int index, int count)
    {
        throw new NotImplementedException();
    }

    public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
    {
        throw new NotImplementedException();
    }

    public override string ReadLine()
    {
        throw new NotImplementedException();
    }

    public override Task<string> ReadLineAsync()
    {
        throw new NotImplementedException();
    }

    public override string ReadToEnd()
    {
        throw new NotImplementedException();
    }

    public override Task<string> ReadToEndAsync()
    {
        throw new NotImplementedException();
    }

    public override string ToString()
    {
        return implementingStreamReader.ToString();
    }
}

Letztendlich ist dies die richtige Idee, aber Ihre Implementierung könnte DRYer sein.
Jodrell

@Jodrell: eine DRY - Version hinzugefügt hier .
Neolisk

1
@Neolisk: Danke! Ich hätte das wahrscheinlich aufräumen sollen, bevor ich es gepostet habe :)
Ryan Adams

4

Regex-basierter Ansatz

public static string StripInvalidXmlCharacters(string str)
{
    var invalidXmlCharactersRegex = new Regex("[^\u0009\u000a\u000d\u0020-\ud7ff\ue000-\ufffd]|([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])");
    return invalidXmlCharactersRegex.Replace(str, "");

}}

Weitere Details finden Sie in meinem Blogpost


1
Dies ist ~ 50x langsamer als die Lösung von dnewcome auf meinem Computer.
Adotout

2

Die oben genannten Lösungen scheinen zum Entfernen ungültiger Zeichen vor der Konvertierung in XML zu dienen.

Verwenden Sie diesen Code, um ungültige XML-Zeichen aus einer XML-Zeichenfolge zu entfernen. z.B. & x1A;

    public static string CleanInvalidXmlChars( string Xml, string XMLVersion )
    {
        string pattern = String.Empty;
        switch( XMLVersion )
        {
            case "1.0":
                pattern = @"&#x((10?|[2-F])FFF[EF]|FDD[0-9A-F]|7F|8[0-46-9A-F]9[0-9A-F]);";
                break;
            case "1.1":
                pattern = @"&#x((10?|[2-F])FFF[EF]|FDD[0-9A-F]|[19][0-9A-F]|7F|8[0-46-9A-F]|0?[1-8BCEF]);";
                break;
            default:
                throw new Exception( "Error: Invalid XML Version!" );
        }

        Regex regex = new Regex( pattern, RegexOptions.IgnoreCase );
        if( regex.IsMatch( Xml ) )
            Xml = regex.Replace( Xml, String.Empty );
        return Xml;
    }

http://balajiramesh.wordpress.com/2008/05/30/strip-illegal-xml-characters-based-on-w3c-standard/


1
-1 Diese Antwort geht nicht auf die gestellte Frage ein und ist in jedem Fall falsch und irreführend, da nur ungültige XML-Zeichenentitätsreferenzen entfernt werden, nicht jedoch ungültige XML-Zeichen.
Daniel Cassidy

1

Geänderte Antwort oder ursprüngliche Antwort von Neolisk oben .
Änderungen: Das Zeichen \ 0 wird übergeben, das Entfernen erfolgt und nicht das Ersetzen. Außerdem wurde die Methode XmlConvert.IsXmlChar (char) verwendet

    /// <summary>
    /// Replaces invalid Xml characters from input file, NOTE: if replacement character is \0, then invalid Xml character is removed, instead of 1-for-1 replacement
    /// </summary>
    public class InvalidXmlCharacterReplacingStreamReader : StreamReader
    {
        private readonly char _replacementCharacter;

        public InvalidXmlCharacterReplacingStreamReader(string fileName, char replacementCharacter)
            : base(fileName)
        {
            _replacementCharacter = replacementCharacter;
        }

        public override int Peek()
        {
            int ch = base.Peek();
            if (ch != -1 && IsInvalidChar(ch))
            {
                if ('\0' == _replacementCharacter)
                    return Peek(); // peek at the next one

                return _replacementCharacter;
            }
            return ch;
        }

        public override int Read()
        {
            int ch = base.Read();
            if (ch != -1 && IsInvalidChar(ch))
            {
                if ('\0' == _replacementCharacter)
                    return Read(); // read next one

                return _replacementCharacter;
            }
            return ch;
        }

        public override int Read(char[] buffer, int index, int count)
        {
            int readCount= 0, ch;

            for (int i = 0; i < count && (ch = Read()) != -1; i++)
            {
                readCount++;
                buffer[index + i] = (char)ch;
            }

            return readCount;
        }


        private static bool IsInvalidChar(int ch)
        {
            return !XmlConvert.IsXmlChar((char)ch);
        }
    }

1

Ich habe eine leicht aktualisierte Version von @ Neolisk's Antwort erstellt , die die *AsyncFunktionen unterstützt und die .Net 4.0- XmlConvert.IsXmlCharFunktion verwendet.

public class InvalidXmlCharacterReplacingStreamReader : StreamReader
{
    private readonly char _replacementCharacter;

    public InvalidXmlCharacterReplacingStreamReader(string fileName, char replacementCharacter) : base(fileName)
    {
        _replacementCharacter = replacementCharacter;
    }

    public InvalidXmlCharacterReplacingStreamReader(Stream stream, char replacementCharacter) : base(stream)
    {
        _replacementCharacter = replacementCharacter;
    }

    public override int Peek()
    {
        var ch = base.Peek();
        if (ch != -1 && IsInvalidChar(ch))
        {
            return _replacementCharacter;
        }
        return ch;
    }

    public override int Read()
    {
        var ch = base.Read();
        if (ch != -1 && IsInvalidChar(ch))
        {
            return _replacementCharacter;
        }
        return ch;
    }

    public override int Read(char[] buffer, int index, int count)
    {
        var readCount = base.Read(buffer, index, count);
        ReplaceInBuffer(buffer, index, readCount);
        return readCount;
    }

    public override async Task<int> ReadAsync(char[] buffer, int index, int count)
    {
        var readCount = await base.ReadAsync(buffer, index, count).ConfigureAwait(false);
        ReplaceInBuffer(buffer, index, readCount);
        return readCount;
    }

    private void ReplaceInBuffer(char[] buffer, int index, int readCount)
    {
        for (var i = index; i < readCount + index; i++)
        {
            var ch = buffer[i];
            if (IsInvalidChar(ch))
            {
                buffer[i] = _replacementCharacter;
            }
        }
    }

    private static bool IsInvalidChar(int ch)
    {
        return IsInvalidChar((char)ch);
    }

    private static bool IsInvalidChar(char ch)
    {
        return !XmlConvert.IsXmlChar(ch);
    }
}

0

Verwenden Sie diese Funktion, um ungültige XML-Zeichen zu entfernen.

public static string CleanInvalidXmlChars(string text)   
{   
       string re = @"[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-x10FFFF]";   
       return Regex.Replace(text, re, "");   
} 

-1
private static String removeNonUtf8CompliantCharacters( final String inString ) {
    if (null == inString ) return null;
    byte[] byteArr = inString.getBytes();
    for ( int i=0; i < byteArr.length; i++ ) {
        byte ch= byteArr[i]; 
        // remove any characters outside the valid UTF-8 range as well as all control characters
        // except tabs and new lines
        if ( !( (ch > 31 && ch < 253 ) || ch == '\t' || ch == '\n' || ch == '\r') ) {
            byteArr[i]=' ';
        }
    }
    return new String( byteArr );
}

1
-1 Diese Antwort ist irreführend und falsch, da sie Zeichen entfernt, die sowohl in Unicode als auch in XML gültig sind.
Daniel Cassidy

-1

Sie können Nicht-UTF-Zeichen wie folgt übergeben:

string sFinalString  = "";
string hex = "";
foreach (char ch in UTFCHAR)
{
    int tmp = ch;
   if ((ch < 0x00FD && ch > 0x001F) || ch == '\t' || ch == '\n' || ch == '\r')
    {
    sFinalString  += ch;
    }
    else
    {  
      sFinalString  += "&#" + tmp+";";
    }
}

1
-1 Diese Antwort ist falsch, da sie ungültige XML-Zeichenentitätsreferenzen generiert (z. B. &#1;keine gültige XML-Zeichenentitätsreferenz). Es ist auch irreführend, weil es Zeichen entfernt, die sowohl in Unicode als auch in XML gültig sind.
Daniel Cassidy

ya das ist wahr, aber die obige Lösung ist für, wenn Sie ungültige XML in XML-Datei übergeben möchten, dann wird es funktionieren oder Sie können kein ungültiges XML-Zeichen in XML-Dokument übergeben
Murari Kumar

Sie können ungültige XML-Zeichen in einem XML-Dokument nicht übergeben, egal was Sie tun. Beispielsweise ist das Zeichen U+0001 START OF HEADINGin einem wohlgeformten XML-Dokument nicht zulässig, und selbst wenn Sie versuchen, es als zu maskieren, ist dies in einem wohlgeformten XML-Dokument &#1;immer noch nicht zulässig.
Daniel Cassidy

-5

Versuchen Sie dies für PHP!

$goodUTF8 = iconv("utf-8", "utf-8//IGNORE", $badUTF8);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.