Wie generiert Stack Overflow seine SEO-freundlichen URLs?

253

Was ist ein guter vollständiger regulärer Ausdruck oder ein anderer Prozess, der den Titel annehmen würde:

Wie ändere ich einen Titel, um Teil der URL zu sein, wie z. B. Stapelüberlauf?

und verwandle es in

how-do-you-change-a-title-to-be-part-of-the-url-like-stack-overflow

das wird in den SEO-freundlichen URLs auf Stack Overflow verwendet?

Die Entwicklungsumgebung, die ich verwende, ist Ruby on Rails , aber wenn es andere plattformspezifische Lösungen gibt (.NET, PHP, Django ), würde ich diese auch gerne sehen.

Ich bin sicher, dass ich (oder ein anderer Leser) auf einer anderen Plattform auf das gleiche Problem stoßen werde.

Ich verwende benutzerdefinierte Routen und möchte hauptsächlich wissen, wie die Zeichenfolge so geändert wird, dass alle Sonderzeichen entfernt werden, alles in Kleinbuchstaben geschrieben und alle Leerzeichen ersetzt werden.

wusher
quelle
Was ist mit lustigen Charakteren? Was wirst du dagegen tun? Umlaute? Interpunktion? Diese müssen berücksichtigt werden. Grundsätzlich würde ich einen White-List-Ansatz verwenden, im Gegensatz zu den oben genannten Black-List-Ansätzen: Beschreiben Sie, welche Zeichen Sie zulassen, welche Zeichen Sie konvertieren (in was?) Und ändern Sie den Rest in etwas Sinnvolles (""). . Ich bezweifle, dass Sie dies in einem regulären Ausdruck tun können ... Warum nicht einfach die Zeichen durchlaufen?
Daren Thomas
1
Sollte auf Meta migriert werden ; Da sich Frage und Antwort speziell mit der SO-Implementierung befassen und die akzeptierte Antwort von @JeffAtwood stammt.
CasperOne
19
@casperOne Glaubst du, Jeff darf keinen Nicht-Meta-Ruf haben? Die Frage ist "wie kann man so etwas machen", nicht speziell "wie geht das hier".
Paŭlo Ebermann
@ PaŭloEbermann: Es geht nicht darum, dass Jeff einen Nicht-Meta-Ruf bekommt (wie viel Ruf er hat, ist wirklich nicht mein Anliegen); Der Fragetext bezog sich speziell auf die Implementierung von StackOverflow, weshalb diese auf Meta basiert .
casperOne

Antworten:

300

So machen wir es. Beachten Sie, dass es wahrscheinlich mehr Randbedingungen gibt, als Sie auf den ersten Blick erkennen.

Dies ist die zweite Version, die für 5x mehr Leistung abgerollt wurde (und ja, ich habe sie verglichen). Ich dachte, ich würde es optimieren, weil diese Funktion hunderte Male pro Seite aufgerufen werden kann.

/// <summary>
/// Produces optional, URL-friendly version of a title, "like-this-one". 
/// hand-tuned for speed, reflects performance refactoring contributed
/// by John Gietzen (user otac0n) 
/// </summary>
public static string URLFriendly(string title)
{
    if (title == null) return "";

    const int maxlen = 80;
    int len = title.Length;
    bool prevdash = false;
    var sb = new StringBuilder(len);
    char c;

    for (int i = 0; i < len; i++)
    {
        c = title[i];
        if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
        {
            sb.Append(c);
            prevdash = false;
        }
        else if (c >= 'A' && c <= 'Z')
        {
            // tricky way to convert to lowercase
            sb.Append((char)(c | 32));
            prevdash = false;
        }
        else if (c == ' ' || c == ',' || c == '.' || c == '/' || 
            c == '\\' || c == '-' || c == '_' || c == '=')
        {
            if (!prevdash && sb.Length > 0)
            {
                sb.Append('-');
                prevdash = true;
            }
        }
        else if ((int)c >= 128)
        {
            int prevlen = sb.Length;
            sb.Append(RemapInternationalCharToAscii(c));
            if (prevlen != sb.Length) prevdash = false;
        }
        if (i == maxlen) break;
    }

    if (prevdash)
        return sb.ToString().Substring(0, sb.Length - 1);
    else
        return sb.ToString();
}

Zeigen Sie den Revisionsverlauf dieses Beitrags an, um die vorherige Version des ersetzten Codes anzuzeigen (der jedoch funktional gleichwertig und 5x schneller ist) (klicken Sie auf den Datumslink).

Den RemapInternationalCharToAsciiQuellcode der Methode finden Sie auch hier .

Jeff Atwood
quelle
24
Es wäre schön mit einer Version, die nicht nur akzentuierte Zeichen wie åäö fallen lässt, sondern sie stattdessen auf aao deaktiviert ... ^^
Oskar Duveborn
22
@oskar der Stub dieser RemapInternationalCharToAscii()Funktion ist da meta.stackexchange.com/questions/7435/…
Jeff Atwood
3
Das ist toll. Die einzige Änderung, die ich bisher vorgenommen habe, ist die Änderung "if (i == maxlen) break;" zu werden "wenn (sb.Length == maxlen) break;" Nur für den Fall, dass die Zeichenfolge, die ich übergebe, viele ungültige Zeichen enthält.
Tom Chantler
4
Eine kleine Optimierung: if (prevdash) sb.Length -= 1; return sb.ToString();anstelle der letzten ifAussage.
Mark Hurd
8
@Dommer sb.Length == maxlen break;ist fehlerhaft, wenn das Zeichen auf maxLenght-1 "ß" ist und es in "ss" konvertiert wird, sb.Length == maxlenewird es niemals wahr sein. Es ist besser, stattdessen zu testen (sb.Length > = maxlen).
Henrik Stenbæk
32

Hier ist meine Version von Jeffs Code. Ich habe folgende Änderungen vorgenommen:

  • Die Bindestriche wurden so angehängt, dass sie hinzugefügt werden konnten, und mussten dann entfernt werden, da es sich um das letzte Zeichen in der Zeichenfolge handelte. Das heißt, wir wollen niemals "meine Schnecke". Dies bedeutet eine zusätzliche Zeichenfolgenzuweisung, um sie in diesem Randfall zu entfernen. Ich habe dies durch Delay-Silbentrennung umgangen. Wenn Sie meinen Code mit dem von Jeff vergleichen, ist die Logik dafür leicht zu befolgen.
  • Sein Ansatz basiert ausschließlich auf der Suche und hat viele Zeichen übersehen, die ich in Beispielen gefunden habe, als ich über Stack Overflow recherchiert habe. Um dem entgegenzuwirken, führe ich zuerst einen Normalisierungsdurchlauf durch (AKA-Kollatierung in Meta Stack Overflow-Frage erwähnt. Nicht US-ASCII-Zeichen wurden von der vollständigen (Profil-) URL entfernt ) und ignoriere dann alle Zeichen außerhalb der zulässigen Bereiche. Das funktioniert meistens ...
  • ... Wenn dies nicht der Fall ist, musste ich auch eine Nachschlagetabelle hinzufügen. Wie oben erwähnt, werden einige Zeichen bei Normalisierung keinem niedrigen ASCII-Wert zugeordnet. Anstatt diese fallen zu lassen, habe ich eine manuelle Liste von Ausnahmen, die zweifellos voller Löcher ist, aber besser als nichts. Der Normalisierungscode wurde von Jon Hannas großartigem Beitrag in der Frage "Stapelüberlauf" inspiriert. Wie kann ich Akzente auf einer Zeichenfolge entfernen? .
  • Die Fallkonvertierung ist jetzt auch optional.

    public static class Slug
    {
        public static string Create(bool toLower, params string[] values)
        {
            return Create(toLower, String.Join("-", values));
        }
    
        /// <summary>
        /// Creates a slug.
        /// References:
        /// http://www.unicode.org/reports/tr15/tr15-34.html
        /// /meta/7435/non-us-ascii-characters-dropped-from-full-profile-url/7696#7696
        /// /programming/25259/how-do-you-include-a-webpage-title-as-part-of-a-webpage-url/25486#25486
        /// /programming/3769457/how-can-i-remove-accents-on-a-string
        /// </summary>
        /// <param name="toLower"></param>
        /// <param name="normalised"></param>
        /// <returns></returns>
        public static string Create(bool toLower, string value)
        {
            if (value == null)
                return "";
    
            var normalised = value.Normalize(NormalizationForm.FormKD);
    
            const int maxlen = 80;
            int len = normalised.Length;
            bool prevDash = false;
            var sb = new StringBuilder(len);
            char c;
    
            for (int i = 0; i < len; i++)
            {
                c = normalised[i];
                if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
                {
                    if (prevDash)
                    {
                        sb.Append('-');
                        prevDash = false;
                    }
                    sb.Append(c);
                }
                else if (c >= 'A' && c <= 'Z')
                {
                    if (prevDash)
                    {
                        sb.Append('-');
                        prevDash = false;
                    }
                    // Tricky way to convert to lowercase
                    if (toLower)
                        sb.Append((char)(c | 32));
                    else
                        sb.Append(c);
                }
                else if (c == ' ' || c == ',' || c == '.' || c == '/' || c == '\\' || c == '-' || c == '_' || c == '=')
                {
                    if (!prevDash && sb.Length > 0)
                    {
                        prevDash = true;
                    }
                }
                else
                {
                    string swap = ConvertEdgeCases(c, toLower);
    
                    if (swap != null)
                    {
                        if (prevDash)
                        {
                            sb.Append('-');
                            prevDash = false;
                        }
                        sb.Append(swap);
                    }
                }
    
                if (sb.Length == maxlen)
                    break;
            }
            return sb.ToString();
        }
    
        static string ConvertEdgeCases(char c, bool toLower)
        {
            string swap = null;
            switch (c)
            {
                case 'ı':
                    swap = "i";
                    break;
                case 'ł':
                    swap = "l";
                    break;
                case 'Ł':
                    swap = toLower ? "l" : "L";
                    break;
                case 'đ':
                    swap = "d";
                    break;
                case 'ß':
                    swap = "ss";
                    break;
                case 'ø':
                    swap = "o";
                    break;
                case 'Þ':
                    swap = "th";
                    break;
            }
            return swap;
        }
    }

Für weitere Details, die Unit-Tests und eine Erklärung, warum das URL- Schema von Facebook etwas intelligenter ist als Stack Overflows, habe ich eine erweiterte Version davon in meinem Blog .

DanH
quelle
4
+1 Das ist großartig, Dan. Ich habe auch einen Kommentar in Ihrem Blog hinzugefügt, in dem es darum geht, möglicherweise zu ändern if (i == maxlen) break;, if (sb.Length == maxlen) break;damit Sie, wenn Sie eine Zeichenfolge mit vielen Leerzeichen / ungültigen Zeichen übergeben, immer noch einen Slug mit der gewünschten Länge erhalten, während der Code in seiner jetzigen Form möglicherweise endet massives Abschneiden (z. B. den Fall betrachten, in dem Sie mit 80 Leerzeichen beginnen ...). Ein grober Benchmark von 10.000.000 Iterationen gegen Jeffs Code zeigte, dass er ungefähr gleich schnell ist.
Tom Chantler
1
Danke, antwortete auf meinem Blog und korrigierte den Code dort und darüber. Vielen Dank auch für das Benchmarking des Codes. Für die Interessierten war es auf Augenhöhe mit Jeffs.
DanH
2
Es scheint, dass es einige Probleme mit Slug.Create () gibt: Großbuchstabenversionen von ÆØÅ werden nicht richtig konvertiert. ÆØ wird ignoriert, während Å in a übersetzt wird. Normalerweise konvertieren Sie "å" in "aa", "ø" in "oe" und "æ" in "ae". Zweite (sb.Length == maxlen) Pause; ist fehlerhaft, wenn das Vorzeichen auf maxLenght-1 "ß" ist (sb.Length == maxlen), wird es niemals wahr sein. Es ist besser, stattdessen auf (sb.Length> = maxlen) zu testen. Ich bin unterdrückt, dass Sie auf eine beliebige Position schneiden und nicht auf das letzte "-", dies erspart Ihnen das Ende mit einem nicht gewünschten Wort am Ende: als müssten Sie nach den letzten "s" schneiden, um zu behaupten " "
Henrik Stenbæk
@DanH Es wäre toll, eine Javascript-Version des Codes zu haben.
Freshblood
16

Sie möchten eine benutzerdefinierte Route einrichten, um die URL auf den Controller zu verweisen, der sie verarbeitet. Da Sie Ruby on Rails verwenden, finden Sie hier eine Einführung in die Verwendung der Routing-Engine.

In Ruby benötigen Sie einen regulären Ausdruck, wie Sie ihn bereits kennen. Hier ist der reguläre Ausdruck, den Sie verwenden können:

def permalink_for(str)
    str.gsub(/[^\w\/]|[!\(\)\.]+/, ' ').strip.downcase.gsub(/\ +/, '-')
end
Dale Ragan
quelle
11

Sie können diese JavaScript- Funktion auch zur In-Form-Generierung der Slugs verwenden (diese basiert auf Django / wird von Django kopiert ):

function makeSlug(urlString, filter) {
    // Changes, e.g., "Petty theft" to "petty_theft".
    // Remove all these words from the string before URLifying

    if(filter) {
        removelist = ["a", "an", "as", "at", "before", "but", "by", "for", "from",
        "is", "in", "into", "like", "of", "off", "on", "onto", "per",
        "since", "than", "the", "this", "that", "to", "up", "via", "het", "de", "een", "en",
        "with"];
    }
    else {
        removelist = [];
    }
    s = urlString;
    r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi');
    s = s.replace(r, '');
    s = s.replace(/[^-\w\s]/g, ''); // Remove unneeded characters
    s = s.replace(/^\s+|\s+$/g, ''); // Trim leading/trailing spaces
    s = s.replace(/[-\s]+/g, '-'); // Convert spaces to hyphens
    s = s.toLowerCase(); // Convert to lowercase
    return s; // Trim to first num_chars characters
}
fijter
quelle
Das Hinzufügen einiger Lets oder Consts wäre großartig, da dies nicht Vanilla JS ist.
Aditya Anand
8

Hier ist die PHP-Funktion in WordPress, die das macht ... Ich würde denken, dass WordPress eine der beliebtesten Plattformen ist, die ausgefallene Links verwendet.

    Funktion sanitize_title_with_dashes ($ title) {
            $ title = strip_tags ($ title);
            // Escape-Oktette beibehalten.
            $ title = preg_replace ('|% ([a-fA-F0-9] [a-fA-F0-9]) |', '--- $ 1 ---', $ title);
            // Prozentzeichen entfernen, die nicht Teil eines Oktetts sind.
            $ title = str_replace ('%', '', $ title);
            // Oktette wiederherstellen.
            $ title = preg_replace ('| --- ([a-fA-F0-9] [a-fA-F0-9]) --- |', '% $ 1', $ title);
            $ title = remove_accents ($ title);
            if (scheint_utf8 ($ title)) {
                    if (function_exists ('mb_strtolower')) {
                            $ title = mb_strtolower ($ title, 'UTF-8');
                    }}
                    $ title = utf8_uri_encode ($ title, 200);
            }}
            $ title = strtolower ($ title);
            $ title = preg_replace ('/&.+?;/', '', $ title); // Entitäten töten
            $ title = preg_replace ('/ [^% a-z0-9 _-] /', '', $ title);
            $ title = preg_replace ('/ \ s + /', '-', $ title);
            $ title = preg_replace ('| - + |', '-', $ title);
            $ title = trim ($ title, '-');
            $ title zurückgeben;
    }}

Diese Funktion sowie einige der unterstützenden Funktionen finden Sie in wp-includes / formatting.php.

Der How-To-Geek
quelle
6
Dies ist keine vollständige Antwort. Ihnen fehlen Funktionen wie : remove_accents, seems_utf8...
Nikola Loncar
Um die Antwort von @The How-To Geek zu vervollständigen, können git clone git://core.git.wordpress.org/Sie die wp-includes/formatting.phpDatei immer noch in
mickro
5

Wenn Sie Rails Edge verwenden, können Sie sich auf Inflector.parametrize verlassen - hier das Beispiel aus der Dokumentation:

  class Person
    def to_param
      "#{id}-#{name.parameterize}"
    end
  end

  @person = Person.find(1)
  # => #<Person id: 1, name: "Donald E. Knuth">

  <%= link_to(@person.name, person_path(@person)) %>
  # => <a href="https://stackoverflow.com/person/1-donald-e-knuth">Donald E. Knuth</a>

Auch wenn Sie mit exotischeren Zeichen wie Akzenten (éphémère) in früheren Versionen von Rails umgehen müssen, können Sie eine Mischung aus PermalinkFu und DiacriticsFu verwenden :

DiacriticsFu::escape("éphémère")
=> "ephemere"

DiacriticsFu::escape("räksmörgås")
=> "raksmorgas"
Thibaut Barrère
quelle
5

Ich bin nicht mit Ruby on Rails vertraut, aber das Folgende ist (ungetesteter) PHP-Code. Sie können dies wahrscheinlich sehr schnell in Ruby on Rails übersetzen, wenn Sie es nützlich finden.

$sURL = "This is a title to convert to URL-format. It has 1 number in it!";
// To lower-case
$sURL = strtolower($sURL);

// Replace all non-word characters with spaces
$sURL = preg_replace("/\W+/", " ", $sURL);

// Remove trailing spaces (so we won't end with a separator)
$sURL = trim($sURL);

// Replace spaces with separators (hyphens)
$sURL = str_replace(" ", "-", $sURL);

echo $sURL;
// outputs: this-is-a-title-to-convert-to-url-format-it-has-1-number-in-it

Ich hoffe das hilft.

Vegard Larsen
quelle
4

Ich habe nicht viel mit Ruby oder Rails zu tun, aber in Perl würde ich Folgendes tun:

my $title = "How do you change a title to be part of the url like Stackoverflow?";

my $url = lc $title;   # Change to lower case and copy to URL.
$url =~ s/^\s+//g;     # Remove leading spaces.
$url =~ s/\s+$//g;     # Remove trailing spaces.
$url =~ s/\s+/\-/g;    # Change one or more spaces to single hyphen.
$url =~ s/[^\w\-]//g;  # Remove any non-word characters.

print "$title\n$url\n";

Ich habe gerade einen kurzen Test gemacht und es scheint zu funktionieren. Hoffentlich ist dies relativ einfach in Ruby zu übersetzen.

Brian
quelle
4

T-SQL-Implementierung, angepasst von dbo.UrlEncode :

CREATE FUNCTION dbo.Slug(@string varchar(1024))
RETURNS varchar(3072)
AS
BEGIN
    DECLARE @count int, @c char(1), @i int, @slug varchar(3072)

    SET @string = replace(lower(ltrim(rtrim(@string))),' ','-')

    SET @count = Len(@string)
    SET @i = 1
    SET @slug = ''

    WHILE (@i <= @count)
    BEGIN
        SET @c = substring(@string, @i, 1)

        IF @c LIKE '[a-z0-9--]'
            SET @slug = @slug + @c

        SET @i = @i +1
    END

    RETURN @slug
END
Sören Kuklau
quelle
4

Ich weiß, dass es eine sehr alte Frage ist, aber da die meisten Browser jetzt Unicode-URLs unterstützen, habe ich in XRegex eine großartige Lösung gefunden , die alles außer Buchstaben konvertiert (in allen Sprachen in '-').

Dies kann in mehreren Programmiersprachen erfolgen.

Das Muster ist \\p{^L}+und dann müssen Sie es nur verwenden, um alle Nicht-Buchstaben durch '-' zu ersetzen.

Arbeitsbeispiel in node.js mit xregex- Modul.

var text = 'This ! can @ have # several $ letters % from different languages such as עברית or Español';

var slugRegEx = XRegExp('((?!\\d)\\p{^L})+', 'g');

var slug = XRegExp.replace(text, slugRegEx, '-').toLowerCase();

console.log(slug) ==> "this-can-have-several-letters-from-different-languages-such-as-עברית-or-español"
Rotem
quelle
3

Angenommen, Ihre Modellklasse verfügt über ein title-Attribut, können Sie die to_param-Methode im Modell einfach wie folgt überschreiben:

def to_param
  title.downcase.gsub(/ /, '-')
end

Diese Railscast-Episode enthält alle Details. Sie können auch sicherstellen, dass der Titel nur gültige Zeichen enthält, indem Sie Folgendes verwenden:

validates_format_of :title, :with => /^[a-z0-9-]+$/,
                    :message => 'can only contain letters, numbers and hyphens'
John Topley
quelle
2

Brians Code in Ruby:

title.downcase.strip.gsub(/\ /, '-').gsub(/[^\w\-]/, '')

downcasein Kleinbuchstaben verwandelt sich die Zeichenfolge, stripentfernt führende und nachfolgende Leerzeichen, der erste gsubAnruf g lobally Unter Instituten Räume mit Bindestrichen und die zweite alles entfernt, der kein Brief oder ein Bindestrich ist.

Sören Kuklau
quelle
2

Es gibt ein kleines Ruby on Rails-Plugin namens PermalinkFu , das dies tut. Die Escape-Methode führt die Umwandlung in eine Zeichenfolge durch, die für eine URL geeignet ist . Schauen Sie sich den Code an. Diese Methode ist recht einfach.

Um Nicht- ASCII- Zeichen zu entfernen , wird die iconv lib verwendet, um von 'utf-8' nach 'ascii // ignore // translit' zu übersetzen. Leerzeichen werden dann in Bindestriche umgewandelt, alles wird verkleinert usw.

Lau
quelle
Obwohl dies perfekt funktioniert, habe ich das Gefühl, dass es nicht sehr effizient ist.
WhyNotHugo
2

Sie können die folgende Hilfsmethode verwenden. Es kann die Unicode-Zeichen konvertieren.

public static string ConvertTextToSlug(string s)
{
    StringBuilder sb = new StringBuilder();

    bool wasHyphen = true;

    foreach (char c in s)
    {
        if (char.IsLetterOrDigit(c))
        {
            sb.Append(char.ToLower(c));
            wasHyphen = false;
        }
        else
            if (char.IsWhiteSpace(c) && !wasHyphen)
            {
                sb.Append('-');
                wasHyphen = true;
            }
    }

    // Avoid trailing hyphens
    if (wasHyphen && sb.Length > 0)
        sb.Length--;

    return sb.ToString().Replace("--","-");
}
Peyman Mehrabani
quelle
2

Hier ist meine (langsamere, aber spaßige) Version von Jeffs Code:

public static string URLFriendly(string title)
{
    char? prevRead = null,
        prevWritten = null;

    var seq = 
        from c in title
        let norm = RemapInternationalCharToAscii(char.ToLowerInvariant(c).ToString())[0]
        let keep = char.IsLetterOrDigit(norm)
        where prevRead.HasValue || keep
        let replaced = keep ? norm
            :  prevWritten != '-' ? '-'
            :  (char?)null
        where replaced != null
        let s = replaced + (prevRead == null ? ""
            : norm == '#' && "cf".Contains(prevRead.Value) ? "sharp"
            : norm == '+' ? "plus"
            : "")
        let _ = prevRead = norm
        from written in s
        let __ = prevWritten = written
        select written;

    const int maxlen = 80;  
    return string.Concat(seq.Take(maxlen)).TrimEnd('-');
}

public static string RemapInternationalCharToAscii(string text)
{
    var seq = text.Normalize(NormalizationForm.FormD)
        .Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark);

    return string.Concat(seq).Normalize(NormalizationForm.FormC);
}

Mein Teststring:

" I love C#, F#, C++, and... Crème brûlée!!! They see me codin'... they hatin'... tryin' to catch me codin' dirty... "

Ronnie Overby
quelle
2

Die Stackoverflow-Lösung ist großartig, aber moderne Browser (wie üblich ohne IE) können jetzt gut mit der utf8-Codierung umgehen:

Geben Sie hier die Bildbeschreibung ein

Also habe ich die vorgeschlagene Lösung aktualisiert:

public static string ToFriendlyUrl(string title, bool useUTF8Encoding = false)
{
    ...

        else if (c >= 128)
        {
            int prevlen = sb.Length;
            if (useUTF8Encoding )
            {
                sb.Append(HttpUtility.UrlEncode(c.ToString(CultureInfo.InvariantCulture),Encoding.UTF8));
            }
            else
            {
                sb.Append(RemapInternationalCharToAscii(c));
            }
    ...
}

Vollständiger Code auf Pastebin

Bearbeiten: Hier ist der Code für die RemapInternationalCharToAsciiMethode (der im Pastebin fehlt).

Giammin
quelle
Laut Wikipedia gehörten Mozilla 1.4, Netscape 7.1 und Opera 7.11 zu den ersten Anwendungen, die IDNA unterstützen. Für Internet Explorer 6 steht ein Browser-Plugin zur Verfügung, das IDN-Unterstützung bietet. Die URL-APIs von Internet Explorer 7.0 und Windows Vista bieten native Unterstützung für IDN. Klingt so, als wäre das Entfernen von UTF-8-Zeichen Zeitverschwendung. Es lebe UTF-8 !!!
Muhammad Rehan Saeed
1

Mir hat die Art und Weise gefallen, wie dies ohne Verwendung regulärer Ausdrücke gemacht wird , deshalb habe ich es auf PHP portiert. Ich habe gerade eine Funktion is_betweenzum Überprüfen von Zeichen hinzugefügt :

function is_between($val, $min, $max)
{
    $val = (int) $val; $min = (int) $min; $max = (int) $max;

    return ($val >= $min && $val <= $max);
}

function international_char_to_ascii($char)
{
    if (mb_strpos('àåáâäãåa', $char) !== false)
    {
        return 'a';
    }

    if (mb_strpos('èéêëe', $char) !== false)
    {
        return 'e';
    }

    if (mb_strpos('ìíîïi', $char) !== false)
    {
        return 'i';
    }

    if (mb_strpos('òóôõö', $char) !== false)
    {
        return 'o';
    }

    if (mb_strpos('ùúûüuu', $char) !== false)
    {
        return 'u';
    }

    if (mb_strpos('çccc', $char) !== false)
    {
        return 'c';
    }

    if (mb_strpos('zzž', $char) !== false)
    {
        return 'z';
    }

    if (mb_strpos('ssšs', $char) !== false)
    {
        return 's';
    }

    if (mb_strpos('ñn', $char) !== false)
    {
        return 'n';
    }

    if (mb_strpos('ýÿ', $char) !== false)
    {
        return 'y';
    }

    if (mb_strpos('gg', $char) !== false)
    {
        return 'g';
    }

    if (mb_strpos('r', $char) !== false)
    {
        return 'r';
    }

    if (mb_strpos('l', $char) !== false)
    {
        return 'l';
    }

    if (mb_strpos('d', $char) !== false)
    {
        return 'd';
    }

    if (mb_strpos('ß', $char) !== false)
    {
        return 'ss';
    }

    if (mb_strpos('Þ', $char) !== false)
    {
        return 'th';
    }

    if (mb_strpos('h', $char) !== false)
    {
        return 'h';
    }

    if (mb_strpos('j', $char) !== false)
    {
        return 'j';
    }
    return '';
}

function url_friendly_title($url_title)
{
    if (empty($url_title))
    {
        return '';
    }

    $url_title = mb_strtolower($url_title);

    $url_title_max_length   = 80;
    $url_title_length       = mb_strlen($url_title);
    $url_title_friendly     = '';
    $url_title_dash_added   = false;
    $url_title_char = '';

    for ($i = 0; $i < $url_title_length; $i++)
    {
        $url_title_char     = mb_substr($url_title, $i, 1);

        if (strlen($url_title_char) == 2)
        {
            $url_title_ascii    = ord($url_title_char[0]) * 256 + ord($url_title_char[1]) . "\r\n";
        }
        else
        {
            $url_title_ascii    = ord($url_title_char);
        }

        if (is_between($url_title_ascii, 97, 122) || is_between($url_title_ascii, 48, 57))
        {
            $url_title_friendly .= $url_title_char;

            $url_title_dash_added = false;
        }
        elseif(is_between($url_title_ascii, 65, 90))
        {
            $url_title_friendly .= chr(($url_title_ascii | 32));

            $url_title_dash_added = false;
        }
        elseif($url_title_ascii == 32 || $url_title_ascii == 44 || $url_title_ascii == 46 || $url_title_ascii == 47 || $url_title_ascii == 92 || $url_title_ascii == 45 || $url_title_ascii == 47 || $url_title_ascii == 95 || $url_title_ascii == 61)
        {
            if (!$url_title_dash_added && mb_strlen($url_title_friendly) > 0)
            {
                $url_title_friendly .= chr(45);

                $url_title_dash_added = true;
            }
        }
        else if ($url_title_ascii >= 128)
        {
            $url_title_previous_length = mb_strlen($url_title_friendly);

            $url_title_friendly .= international_char_to_ascii($url_title_char);

            if ($url_title_previous_length != mb_strlen($url_title_friendly))
            {
                $url_title_dash_added = false;
            }
        }

        if ($i == $url_title_max_length)
        {
            break;
        }
    }

    if ($url_title_dash_added)
    {
        return mb_substr($url_title_friendly, 0, -1);
    }
    else
    {
        return $url_title_friendly;
    }
}
Peter Mortensen
quelle
1

Jetzt können alle Browser die utf8-Codierung gut verarbeiten, sodass Sie die WebUtility.UrlEncode- Methode verwenden können, die der von @giamin verwendeten HttpUtility.UrlEncode ähnelt, jedoch außerhalb einer Webanwendung funktioniert.

ikourfaln
quelle
1

Ich habe den Code nach TypeScript portiert. Es kann leicht an JavaScript angepasst werden.

Ich füge .containsdem StringPrototyp eine Methode hinzu. Wenn Sie auf die neuesten Browser oder ES6 abzielen, können Sie diese .includesstattdessen verwenden.

if (!String.prototype.contains) {
    String.prototype.contains = function (check) {
        return this.indexOf(check, 0) !== -1;
    };
}

declare interface String {
    contains(check: string): boolean;
}

export function MakeUrlFriendly(title: string) {
            if (title == null || title == '')
                return '';

            const maxlen = 80;
            let len = title.length;
            let prevdash = false;
            let result = '';
            let c: string;
            let cc: number;
            let remapInternationalCharToAscii = function (c: string) {
                let s = c.toLowerCase();
                if ("àåáâäãåą".contains(s)) {
                    return "a";
                }
                else if ("èéêëę".contains(s)) {
                    return "e";
                }
                else if ("ìíîïı".contains(s)) {
                    return "i";
                }
                else if ("òóôõöøőð".contains(s)) {
                    return "o";
                }
                else if ("ùúûüŭů".contains(s)) {
                    return "u";
                }
                else if ("çćčĉ".contains(s)) {
                    return "c";
                }
                else if ("żźž".contains(s)) {
                    return "z";
                }
                else if ("śşšŝ".contains(s)) {
                    return "s";
                }
                else if ("ñń".contains(s)) {
                    return "n";
                }
                else if ("ýÿ".contains(s)) {
                    return "y";
                }
                else if ("ğĝ".contains(s)) {
                    return "g";
                }
                else if (c == 'ř') {
                    return "r";
                }
                else if (c == 'ł') {
                    return "l";
                }
                else if (c == 'đ') {
                    return "d";
                }
                else if (c == 'ß') {
                    return "ss";
                }
                else if (c == 'Þ') {
                    return "th";
                }
                else if (c == 'ĥ') {
                    return "h";
                }
                else if (c == 'ĵ') {
                    return "j";
                }
                else {
                    return "";
                }
            };

            for (let i = 0; i < len; i++) {
                c = title[i];
                cc = c.charCodeAt(0);

                if ((cc >= 97 /* a */ && cc <= 122 /* z */) || (cc >= 48 /* 0 */ && cc <= 57 /* 9 */)) {
                    result += c;
                    prevdash = false;
                }
                else if ((cc >= 65 && cc <= 90 /* A - Z */)) {
                    result += c.toLowerCase();
                    prevdash = false;
                }
                else if (c == ' ' || c == ',' || c == '.' || c == '/' || c == '\\' || c == '-' || c == '_' || c == '=') {
                    if (!prevdash && result.length > 0) {
                        result += '-';
                        prevdash = true;
                    }
                }
                else if (cc >= 128) {
                    let prevlen = result.length;
                    result += remapInternationalCharToAscii(c);
                    if (prevlen != result.length) prevdash = false;
                }
                if (i == maxlen) break;
            }

            if (prevdash)
                return result.substring(0, result.length - 1);
            else
                return result;
        }
Sam
quelle
0

Nein nein Nein. Sie sind alle so sehr falsch. Abgesehen von den Diakritika-Fu-Sachen kommen Sie dorthin, aber was ist mit asiatischen Charakteren (Schande für Ruby-Entwickler, dass sie ihre Nihonjin nicht in Betracht ziehen Brüder ).

Firefox und Safari zeigen beide Nicht-ASCII-Zeichen in der URL an und sehen ehrlich gesagt großartig aus. Es ist schön, Links wie ' http://somewhere.com/news/read/ ' zu unterstützen '.

Hier ist also ein PHP-Code, der das macht, aber ich habe ihn gerade geschrieben und ihn nicht auf Stress getestet.

<?php
    function slug($str)
    {
        $args = func_get_args();
        array_filter($args);  //remove blanks
        $slug = mb_strtolower(implode('-', $args));

        $real_slug = '';
        $hyphen = '';
        foreach(SU::mb_str_split($slug) as $c)
        {
            if (strlen($c) > 1 && mb_strlen($c)===1)
            {
                $real_slug .= $hyphen . $c;
                $hyphen = '';
            }
            else
            {
                switch($c)
                {
                    case '&':
                        $hyphen = $real_slug ? '-and-' : '';
                        break;
                    case 'a':
                    case 'b':
                    case 'c':
                    case 'd':
                    case 'e':
                    case 'f':
                    case 'g':
                    case 'h':
                    case 'i':
                    case 'j':
                    case 'k':
                    case 'l':
                    case 'm':
                    case 'n':
                    case 'o':
                    case 'p':
                    case 'q':
                    case 'r':
                    case 's':
                    case 't':
                    case 'u':
                    case 'v':
                    case 'w':
                    case 'x':
                    case 'y':
                    case 'z':

                    case 'A':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'E':
                    case 'F':
                    case 'G':
                    case 'H':
                    case 'I':
                    case 'J':
                    case 'K':
                    case 'L':
                    case 'M':
                    case 'N':
                    case 'O':
                    case 'P':
                    case 'Q':
                    case 'R':
                    case 'S':
                    case 'T':
                    case 'U':
                    case 'V':
                    case 'W':
                    case 'X':
                    case 'Y':
                    case 'Z':

                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        $real_slug .= $hyphen . $c;
                        $hyphen = '';
                        break;

                    default:
                       $hyphen = $hyphen ? $hyphen : ($real_slug ? '-' : '');
                }
            }
        }
        return $real_slug;
    }

Beispiel:

$str = "~!@#$%^&*()_+-=[]\{}|;':\",./<>?\n\r\t\x07\x00\x04 コリン ~!@#$%^&*()_+-=[]\{}|;':\",./<>?\n\r\t\x07\x00\x04 トーマス ~!@#$%^&*()_+-=[]\{}|;':\",./<>?\n\r\t\x07\x00\x04 アーノルド ~!@#$%^&*()_+-=[]\{}|;':\",./<>?\n\r\t\x07\x00\x04";
echo slug($str);

Ausgänge: コ リ and -und- ト ー and and -und- ア ー ノ ル ド

Das '-und-' liegt daran, dass & in '-und-' geändert wird.

Peter Mortensen
quelle
4
Ich weiß wirklich nicht, was ich zu dieser Information sagen soll.
Sjas
3
Das ist ein wirklich gutes Beispiel dafür, wann eine switch case-Anweisung NICHT verwendet werden sollte.
NickG