Bagaimana Stack Overflow menghasilkan URL yang ramah-SEO?

253

Apa ekspresi reguler lengkap yang bagus atau proses lain yang akan mengambil judul:

Bagaimana Anda mengubah judul menjadi bagian dari URL seperti Stack Overflow?

dan mengubahnya menjadi

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

yang digunakan dalam URL ramah-SEO di Stack Overflow?

Lingkungan pengembangan yang saya gunakan adalah Ruby on Rails , tetapi jika ada beberapa solusi khusus platform (.NET, PHP, Django ), saya akan senang melihatnya juga.

Saya yakin saya (atau pembaca lain) akan menemukan masalah yang sama pada platform yang berbeda di telepon.

Saya menggunakan rute khusus, dan saya terutama ingin tahu cara mengubah string agar semua karakter khusus dihapus, semuanya huruf kecil, dan semua spasi putih diganti.

lebih bijaksana
sumber
Bagaimana dengan karakter lucu? Apa yang akan kamu lakukan dengan itu? Umlaut? Tanda baca? Ini perlu dipertimbangkan. Pada dasarnya, saya akan menggunakan pendekatan daftar putih, yang bertentangan dengan pendekatan daftar hitam di atas: Jelaskan karakter mana yang akan Anda izinkan, karakter mana yang akan Anda konversi (untuk apa?) Dan kemudian ubah sisanya menjadi sesuatu yang bermakna ("") . Saya ragu Anda dapat melakukan ini dalam satu regex ... Mengapa tidak hanya mengulang-ulang karakter?
Daren Thomas
1
Harus dimigrasikan ke meta ; sebagai pertanyaan dan jawaban keduanya secara khusus berhubungan dengan implementasi SO, dan jawaban yang diterima adalah dari @JeffAtwood.
casperOne
19
@casperOne Apakah Anda pikir Jeff tidak diizinkan memiliki reputasi non-meta? Pertanyaannya adalah tentang "bagaimana seseorang dapat melakukan sesuatu seperti ini", tidak secara khusus "bagaimana ini dilakukan di sini".
Paŭlo Ebermann
@ PaŭloEbermann: Ini bukan tentang Jeff mendapatkan reputasi non-meta (berapa banyak reputasi yang dia miliki sebenarnya bukan urusan saya); badan pertanyaan secara khusus mereferensikan implementasi StackOverflow karena itu alasannya adalah pada meta.
casperOne

Jawaban:

300

Begini cara kami melakukannya. Perhatikan bahwa mungkin ada kondisi tepi lebih dari yang Anda sadari pada pandangan pertama.

Ini adalah versi kedua, membuka gulungan untuk kinerja 5x lebih banyak (dan ya, saya membandingkannya). Saya pikir saya akan mengoptimalkannya karena fungsi ini dapat dipanggil ratusan kali per halaman.

/// <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();
}

Untuk melihat versi sebelumnya dari kode yang diganti ini (tetapi secara fungsional setara dengan, dan 5x lebih cepat), lihat riwayat revisi posting ini (klik tautan tanggal).

Juga, RemapInternationalCharToAsciikode sumber metode dapat ditemukan di sini .

Jeff Atwood
sumber
24
Akan lebih baik dengan versi yang tidak hanya menjatuhkan karakter beraksen seperti åäö tetapi sebaliknya membuat mereka tidak menjadi ... ^^
Oskar Duveborn
22
@oskar tulisan rintisan RemapInternationalCharToAscii()fungsi itu ada di sana meta.stackexchange.com/questions/7435/…
Jeff Atwood
3
Ini bagus. Satu-satunya perubahan yang saya buat sejauh ini adalah mengubah "if (i == maxlen) break;" menjadi "if (sb.Length == maxlen) break;" kalau-kalau ada banyak karakter yang tidak valid dalam string saya lewat.
Tom Chantler
4
Optimalisasi minor: if (prevdash) sb.Length -= 1; return sb.ToString();alih-alih ifpernyataan terakhir .
Mark Hurd
8
@Dommer sb.Length == maxlen break;buggy jika tanda pada maxLenght-1 adalah "ß" ia akan dikonversi menjadi "ss" sb.Length == maxlenetidak akan pernah benar, lebih baik daripada menguji (sb.Length > = maxlen).
Henrik Stenbæk
32

Ini adalah versi saya dari kode Jeff. Saya telah membuat perubahan berikut:

  • Tanda hubung ditambahkan sedemikian rupa sehingga seseorang dapat ditambahkan, dan kemudian perlu menghapus karena itu adalah karakter terakhir dalam string. Yaitu, kita tidak pernah menginginkan "my-slug-". Ini berarti alokasi string tambahan untuk menghapusnya pada kasing tepi ini. Saya telah mengatasi ini dengan menunda-menunda. Jika Anda membandingkan kode saya dengan Jeff, logika untuk ini mudah diikuti.
  • Pendekatannya murni berbasis pencarian dan melewatkan banyak karakter yang saya temukan dalam contoh saat meneliti tentang Stack Overflow. Untuk mengatasi hal ini, pertama-tama saya akan melakukan normalisasi pass (pemeriksaan AKA yang disebutkan dalam pertanyaan Meta Stack Overflow, bukan karakter US-ASCII yang dihapus dari URL (profil) lengkap ), dan kemudian mengabaikan karakter apa pun di luar rentang yang dapat diterima. Ini berfungsi sebagian besar waktu ...
  • ... Untuk saat tidak, saya juga harus menambahkan tabel pencarian. Seperti disebutkan di atas, beberapa karakter tidak memetakan ke nilai ASCII yang rendah ketika dinormalisasi. Daripada menjatuhkan ini, saya punya daftar manual pengecualian yang pasti penuh dengan lubang, tetapi lebih baik daripada tidak sama sekali. Kode normalisasi terinspirasi oleh posting hebat Jon Hanna di pertanyaan Stack Overflow. Bagaimana saya bisa menghilangkan aksen pada string? .
  • Konversi kasus sekarang juga opsional.

    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;
        }
    }

Untuk lebih jelasnya, unit test, dan penjelasan mengapa Facebook 's URL skema adalah pintar sedikit dari Stack Overflows, aku punya versi yang diperluas ini di blog saya .

DanH
sumber
4
+1 Ini bagus Dan. Saya juga menambahkan komentar di blog Anda tentang perubahan yang mungkin if (i == maxlen) break;terjadi if (sb.Length == maxlen) break;sehingga jika Anda memasukkan string dengan banyak spasi putih / karakter yang tidak valid Anda masih bisa mendapatkan siput dengan panjang yang diinginkan, sedangkan kode yang berdiri mungkin berakhir memotongnya secara besar-besaran (mis. pertimbangkan kasus di mana Anda mulai dengan 80 spasi ...). Dan tolok ukur kasar 10.000.000 iterasi terhadap kode Jeff menunjukkan bahwa kecepatannya kira-kira sama.
Tom Chantler
1
Terima kasih, merespons di blog saya dan memperbaiki kode di sana dan di atas. Terima kasih juga telah membuat tolok ukur kode. Bagi mereka yang tertarik itu setara dengan Jeff.
DanH
2
Sepertinya ada beberapa masalah dengan Slug.Create (): Versi huruf besar ÆØÅ tidak dikonversi dengan benar ÆØ diabaikan sementara Å diterjemahkan ke a. Biasanya Anda akan mengonversi "å" menjadi "aa", "ø" menjadi "oe" dan "æ" menjadi "ae". Istirahat kedua (sb.Length == maxlen); buggy jika tanda pada maxLenght-1 adalah "ß" (sb.Length == maxlen) tidak akan pernah benar itu lebih baik untuk menguji untuk (sb.Length> = maxlen). Saya ditekan bahwa Anda memotong pada posisi acak dan tidak memotong pada "-" terakhir, ini akan menyelamatkan Anda dari berakhir dengan kata yang tidak diinginkan pada akhirnya: seolah-olah Anda harus memotong "untuk menegaskan" setelah yang terakhir "s "
Henrik Stenbæk
@DanH akan lebih baik untuk memiliki versi kode javascript.
Freshblood
16

Anda akan ingin mengatur rute khusus untuk mengarahkan URL ke pengontrol yang akan menanganinya. Karena Anda menggunakan Ruby on Rails, di sini ada pengantar dalam menggunakan mesin perutean mereka.

Di Ruby, Anda akan memerlukan ekspresi reguler seperti yang Anda sudah tahu dan inilah ekspresi reguler untuk digunakan:

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

Anda juga dapat menggunakan fungsi JavaScript ini untuk menghasilkan siput dalam-bentuk (yang ini berdasarkan / disalin dari Django ):

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
sumber
Menambahkan beberapa let atau const akan lebih baik karena ini bukan vanilla JS.
Aditya Anand
8

Sebagai tambahan, inilah fungsi PHP di WordPress yang melakukannya ... Saya pikir WordPress adalah salah satu platform yang lebih populer yang menggunakan tautan mewah.

    function sanitize_title_with_dashes ($ title) {
            $ title = strip_tags ($ title);
            // Simpan oktet yang lolos.
            $ title = preg_replace ('|% ([a-fA-F0-9] [a-fA-F0-9]) |', '--- $ 1 ---', $ title);
            // Hapus tanda persen yang bukan bagian dari oktet.
            $ title = str_replace ('%', '', $ title);
            // Kembalikan oktet.
            $ title = preg_replace ('| --- ([a-fA-F0-9] [a-fA-F0-9]) --- |', '% $ 1', $ title);
            $ title = remove_accents ($ title);
            if (looks_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); // bunuh entitas
            $ title = preg_replace ('/ [^% a-z0-9 _-] /', '', $ title);
            $ title = preg_replace ('/ \ s + /', '-', $ title);
            $ title = preg_replace ('| - + |', '-', $ title);
            $ title = trim ($ title, '-');
            mengembalikan $ title;
    }

Fungsi ini dan juga beberapa fungsi pendukung dapat ditemukan di wp-include / formatting.php.

The How-To Geek
sumber
6
Ini bukan jawaban lengkap. Anda kehilangan fungsi seperti: remove_accents, seems_utf8...
Nikola Loncar
untuk menyelesaikan @The How-To Geek menjawab Anda masih dapat git clone git://core.git.wordpress.org/dan menemukan wp-includes/formatting.phpfile ke
mickro
5

Jika Anda menggunakan tepi Rails, Anda dapat mengandalkan Inflector.parametrize - inilah contoh dari dokumentasi:

  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>

Juga jika Anda perlu menangani karakter yang lebih eksotis seperti aksen (éphémère) di versi Rails sebelumnya, Anda dapat menggunakan campuran PermalinkFu dan DiacriticsFu :

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

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

Saya tidak terbiasa dengan Ruby on Rails, tetapi yang berikut adalah (tidak teruji) kode PHP. Anda mungkin dapat menerjemahkan ini dengan sangat cepat ke Ruby on Rails jika Anda merasa berguna.

$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

Saya harap ini membantu.

Vegard Larsen
sumber
4

Saya tidak banyak tentang Ruby atau Rails, tetapi di Perl, inilah yang akan saya lakukan:

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";

Saya baru saja melakukan tes cepat dan sepertinya berhasil. Semoga ini relatif mudah untuk diterjemahkan ke Ruby.

Brian
sumber
4

Implementasi T-SQL, diadaptasi dari 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
sumber
4

Saya tahu ini pertanyaan yang sangat lama, tetapi karena sebagian besar browser sekarang mendukung url unicode, saya menemukan solusi hebat di XRegex yang mengubah segalanya kecuali huruf (dalam semua bahasa menjadi '-').

Itu bisa dilakukan dalam beberapa bahasa pemrograman.

Polanya adalah \\p{^L}+dan kemudian Anda hanya perlu menggunakannya untuk mengganti semua bukan huruf ke '-'.

Contoh kerja di node.js dengan modul xregex .

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
sumber
3

Dengan asumsi bahwa kelas model Anda memiliki atribut judul, Anda dapat dengan mudah menimpa metode to_param dalam model, seperti ini:

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

Episode Railscast ini memiliki semua detail. Anda juga dapat memastikan bahwa judul hanya berisi karakter yang valid menggunakan ini:

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

Kode Brian, di Ruby:

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

downcaseternyata string ke huruf kecil, stripmenghilangkan terkemuka dan trailing spasi, pertama gsubpanggilan g lobally sub ruang stitutes dengan tanda hubung, dan Menghapus kedua segala sesuatu yang tidak surat atau tanda hubung.

Sören Kuklau
sumber
2

Ada plugin Ruby on Rails kecil yang disebut PermalinkFu , yang melakukan ini. The Metode melarikan diri melakukan transformasi menjadi string yang cocok untuk URL . Lihatlah kodenya; Metode itu cukup sederhana.

Untuk menghapus karakter non- ASCII, ia menggunakan ikonv lib untuk menerjemahkan ke 'ascii // diabaikan // translit' dari 'utf-8'. Spasi kemudian berubah menjadi garis putus-putus, semuanya diturunkan, dll.

Lau
sumber
Walaupun ini bekerja dengan sempurna, saya merasa itu tidak efisien.
WhyNotHugo
2

Anda dapat menggunakan metode pembantu berikut. Itu dapat mengkonversi karakter Unicode.

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
sumber
2

Inilah kode Jeff versi saya (lebih lambat, tetapi menyenangkan):

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);
}

String pengujian saya:

" 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
sumber
2

The solusi stackoverflow besar, namun browser modern (tidak termasuk IE, seperti biasa) sekarang menangani encoding baik utf8:

masukkan deskripsi gambar di sini

Jadi saya memutakhirkan solusi yang diusulkan:

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));
            }
    ...
}

Kode Lengkap tentang Pastebin

Sunting: Berikut kode untuk RemapInternationalCharToAsciimetode (yang tidak ada dalam pastebin).

giammin
sumber
Menurut Wikipedia , Mozilla 1.4, Netscape 7.1, Opera 7.11 adalah di antara aplikasi pertama yang mendukung IDNA. Plugin browser tersedia untuk Internet Explorer 6 untuk memberikan dukungan IDN. Internet Explorer 7.0 dan API URL Windows Vista menyediakan dukungan asli untuk IDN. Kedengarannya seperti menghapus karakter UTF-8 adalah buang-buang waktu. Hidup UTF-8 !!!
Muhammad Rehan Saeed
1

Saya suka cara ini dilakukan tanpa menggunakan ekspresi reguler , jadi saya porting ke PHP. Saya baru saja menambahkan fungsi yang dipanggil is_betweenuntuk memeriksa karakter:

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
sumber
1

Sekarang semua Browser menangani pengkodean utf8 dengan baik, sehingga Anda dapat menggunakan Metode WebUtility.UrlEncode , seperti HttpUtility.UrlEncode yang digunakan oleh @giamin tetapi berfungsi di luar aplikasi web.

ikourfaln
sumber
1

Saya mengirim kode ke TypeScript. Itu dapat dengan mudah disesuaikan dengan JavaScript.

Saya menambahkan .containsmetode ke Stringprototipe, jika Anda menargetkan browser terbaru atau ES6 yang dapat Anda gunakan .includessebagai gantinya.

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
sumber
0

Tidak tidak Tidak. Anda semua sangat salah. Kecuali untuk hal-hal diakritik-fu, Anda sudah sampai di sana, tapi bagaimana dengan karakter Asia (malu pada pengembang Ruby karena tidak mempertimbangkan nihonjin mereka saudara-saudara mereka).

Firefox dan Safari keduanya menampilkan karakter non-ASCII di URL , dan terus terang mereka terlihat hebat. Sangat menyenangkan untuk mendukung tautan seperti ' http://somewhere.com/news/read/ お 前 た ち は ア ア ホ じ ゃ な な い か い '.

Jadi, inilah beberapa kode PHP yang akan melakukannya, tetapi saya baru saja menulisnya dan belum stres untuk mengujinya.

<?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;
    }

Contoh:

$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);

Output: コ リ ン -dan- ト ー マ ス -dan- ア ー ノ ル ド

Tanda '-dan-' adalah karena & bisa diubah menjadi '-dan-'.

Peter Mortensen
sumber
4
Saya benar-benar tidak tahu harus berkata apa tentang informasi ini.
sjas
3
Itu contoh yang sangat bagus ketika TIDAK menggunakan pernyataan kasus sakelar.
NickG