Làm thế nào để Stack Overflow tạo ra các URL thân thiện với SEO của nó?


253

Một biểu thức chính quy hoàn chỉnh tốt là gì hoặc một số quy trình khác sẽ có tiêu đề:

Làm cách nào để bạn thay đổi tiêu đề thành một phần của URL như Stack Overflow?

và biến nó thành

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

được sử dụng trong các URL thân thiện với SEO trên Stack Overflow?

Môi trường phát triển tôi đang sử dụng là Ruby on Rails , nhưng nếu có một số giải pháp dành riêng cho nền tảng khác (.NET, PHP, Django ), tôi cũng rất muốn thấy những giải pháp đó.

Tôi chắc chắn rằng tôi (hoặc một người đọc khác) sẽ gặp phải cùng một vấn đề trên một nền tảng khác.

Tôi đang sử dụng các tuyến tùy chỉnh và tôi chủ yếu muốn biết cách thay đổi chuỗi thành tất cả các ký tự đặc biệt, đó là tất cả chữ thường và tất cả khoảng trắng được thay thế.


Còn những nhân vật hài hước thì sao? Bạn sẽ làm gì về những điều đó? Ôi? Chấm câu? Những điều này cần được xem xét. Về cơ bản, tôi sẽ sử dụng cách tiếp cận danh sách trắng, trái ngược với cách tiếp cận danh sách đen ở trên: Mô tả những ký tự bạn sẽ cho phép, những ký tự bạn sẽ chuyển đổi (thành gì?) Và sau đó thay đổi phần còn lại thành một cái gì đó có ý nghĩa ("") . Tôi nghi ngờ bạn có thể làm điều này trong một regex ... Tại sao không chỉ lặp qua các ký tự?
Daren Thomas

1
Nên được chuyển sang meta ; như câu hỏi và câu trả lời cụ thể liên quan đến việc thực hiện SO và câu trả lời được chấp nhận là từ @JeffAtwood.
casperOne

19
@casperOne Bạn có nghĩ Jeff không được phép mang tiếng tăm không? Câu hỏi là về "làm thế nào người ta có thể làm một cái gì đó như thế này", không cụ thể là "làm thế nào được thực hiện ở đây".
Paŭlo Ebermann

@ PaŭloEbermann: Không phải là về việc Jeff có được danh tiếng không phải là meta (anh ta có bao nhiêu danh tiếng thực sự không phải là mối quan tâm của tôi); phần thân câu hỏi đã tham chiếu cụ thể việc triển khai của StackOverflow do đó lý do căn bản của nó là trên meta.
casperOne

Câu trả lời:


300

Đây là cách chúng tôi làm điều đó. Lưu ý rằng có thể có nhiều điều kiện cạnh hơn bạn nhận ra từ cái nhìn đầu tiên.

Đây là phiên bản thứ hai, không được kiểm soát để có hiệu suất cao hơn gấp 5 lần (và vâng, tôi đã điểm chuẩn nó). Tôi hình dung tôi sẽ tối ưu hóa nó bởi vì chức năng này có thể được gọi hàng trăm lần trên mỗi trang.

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

Để xem phiên bản trước của mã này, mã này đã được thay thế (nhưng có chức năng tương đương và nhanh hơn 5x), xem lịch sử sửa đổi của bài đăng này (nhấp vào liên kết ngày).

Ngoài ra, RemapInternationalCharToAsciimã nguồn phương thức có thể được tìm thấy ở đây .


24
Sẽ thật tuyệt với một phiên bản không bỏ các ký tự có dấu như åäö mà thay vào đó làm mất tập trung chúng thành aao ... ^^
Oskar Duveborn

22
@oskar còn sơ khai của RemapInternationalCharToAscii()chức năng đó là có meta.stackexchange.com/questions/7435/ mẹo
Jeff Atwood

3
Điều đó thật tuyệt. Thay đổi duy nhất tôi đã thực hiện cho đến nay là thay đổi "if (i == maxlen) break;" để trở thành "if (sb.Lpm == maxlen) break;" chỉ trong trường hợp có rất nhiều ký tự không hợp lệ trong chuỗi tôi đang truyền vào.
Tom Chantler

4
Một tối ưu hóa nhỏ: if (prevdash) sb.Length -= 1; return sb.ToString();thay vì iftuyên bố cuối cùng .
Đánh dấu Hurd

8
@Dommer sb.Length == maxlen break;là lỗi nếu ký hiệu trên maxLenght-1 là "ß", nó sẽ được chuyển đổi thành "ss" sb.Length == maxlenesẽ không bao giờ đúng, thay vào đó, tốt hơn là nên kiểm tra (sb.Length > = maxlen).
Henrik Stenbæk

32

Đây là phiên bản mã của Jeff. Tôi đã thực hiện các thay đổi sau:

  • Các dấu gạch nối được nối theo cách mà người ta có thể được thêm vào, và sau đó cần loại bỏ vì đó là ký tự cuối cùng trong chuỗi. Đó là, chúng tôi không bao giờ muốn ăn thịt của tôi. Điều này có nghĩa là phân bổ chuỗi bổ sung để loại bỏ nó trong trường hợp cạnh này. Tôi đã làm việc xung quanh điều này bằng cách trì hoãn. Nếu bạn so sánh mã của tôi với Jeff thì logic này rất dễ thực hiện.
  • Cách tiếp cận của anh ta hoàn toàn dựa trên tra cứu và bỏ lỡ rất nhiều nhân vật tôi tìm thấy trong các ví dụ khi nghiên cứu về Stack Overflow. Để khắc phục điều này, trước tiên tôi xác định một vượt qua chuẩn hóa (đối chiếu AKA được đề cập trong câu hỏi Meta Stack Overflow Các ký tự không phải US-ASCII được thả từ URL (hồ sơ) đầy đủ ), sau đó bỏ qua bất kỳ ký tự nào ngoài phạm vi chấp nhận được. Điều này hoạt động hầu hết thời gian ...
  • ... Vì khi đó tôi không phải thêm bảng tra cứu. Như đã đề cập ở trên, một số ký tự không ánh xạ tới giá trị ASCII thấp khi được chuẩn hóa. Thay vì bỏ những thứ này, tôi đã có một danh sách các trường hợp ngoại lệ thủ công không nghi ngờ gì là đầy lỗ hổng, nhưng tốt hơn là không có gì. Mã chuẩn hóa được lấy cảm hứng từ bài đăng tuyệt vời của Jon Hanna trong câu hỏi Stack Overflow Làm cách nào tôi có thể xóa dấu trên chuỗi? .
  • Việc chuyển đổi trường hợp bây giờ cũng là tùy chọn.

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

Để biết thêm chi tiết, đơn vị kiểm tra và giải thích lý do tại sao lược đồ URL của Facebook thông minh hơn một chút so với Stack Overflows, tôi đã có phiên bản mở rộng này trên blog của mình .


4
+1 Đây là Dan tuyệt vời. Tôi cũng đã thêm một nhận xét trên blog của bạn về khả năng thay đổi if (i == maxlen) break;thànhif (sb.Length == maxlen) break; thay vào đó để nếu bạn chuyển vào một chuỗi có nhiều khoảng trắng / ký tự không hợp lệ, bạn vẫn có thể nhận được một sên có độ dài mong muốn, trong khi mã có thể kết thúc ồ ạt cắt ngắn nó (ví dụ như xem xét trường hợp bạn bắt đầu với 80 khoảng trắng ...). Và một điểm chuẩn sơ bộ của 10.000.000 lần lặp so với mã của Jeff cho thấy nó có tốc độ tương đương.
Tom Chantler

1
Cảm ơn, đã trả lời trên blog của tôi và sửa mã anh ấy ở đó và ở trên. Cũng cảm ơn cho điểm chuẩn mã. Đối với những người quan tâm, nó ngang tầm với Jeff's.
DanH

2
Có vẻ như có một số vấn đề với Slug.Create (): Các phiên bản chữ hoa của không được chuyển đổi chính xác bị bỏ qua trong khi được dịch sang a. Thông thường, bạn sẽ chuyển đổi từ ã ra thành một trò chơi, đó là chuyển sang chế độ ăn chơi và thời trang. Thứ hai (sb.Lpm == maxlen) break; là lỗi nếu ký hiệu trên maxLenght-1 là "ß" (sb.Lpm == maxlen) sẽ không bao giờ đúng, thay vào đó, tốt hơn là kiểm tra (sb.Lipse> = maxlen). Tôi bị ức chế rằng bạn cắt bất kỳ vị trí ngẫu nhiên nào và không cắt vào Lần cuối cùng - Nhận, điều này sẽ cứu bạn khỏi kết thúc bằng một từ không mong muốn cuối cùng: như thể bạn phải cắt giảm để xác nhận phạm lỗi sau lần cuối cùng "
Henrik Stenbæk

@DanH thật tuyệt vời khi có phiên bản javascript của mã.
Freshblood

16

Bạn sẽ muốn thiết lập một tuyến tùy chỉnh để trỏ URL tới bộ điều khiển sẽ xử lý nó. Vì bạn đang sử dụng Ruby on Rails, đây là phần giới thiệu về cách sử dụng công cụ định tuyến của họ.

Trong Ruby, bạn sẽ cần một biểu thức chính quy như bạn đã biết và đây là biểu thức chính quy để sử dụng:

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

11

Bạn cũng có thể sử dụng chức năng JavaScript này để tạo sên dưới dạng (cái này dựa trên / sao chép từ 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
}

Thêm một số cho phép hoặc const sẽ là tuyệt vời vì đây không phải là vanilla JS.
Aditya Anand

8

Để đo lường tốt, đây là chức năng PHP trong WordPress thực hiện điều đó ... Tôi nghĩ rằng WordPress là một trong những nền tảng phổ biến hơn sử dụng các liên kết ưa thích.

    hàm sanitize_title_with_dashes ($ title) {
            $ title = dải_tags ($ title);
            // Bảo toàn các octet thoát.
            $ title = preg numplace ('|% ([a-fA-F0-9] [a-fA-F0-9]) |', '--- $ 1 ---', $ title);
            // Xóa các dấu phần trăm không phải là một phần của octet.
            $ title = str numplace ('%', '', $ title);
            // Khôi phục các octet.
            $ title = preg numplace ('| --- ([a-fA-F0-9] [a-fA-F0-9]) --- |', '% $ 1', $ title);
            $ title = remove_accents ($ title);
            if (dường như_utf8 ($ title)) {
                    if (function_exists ('mb_strtolower')) {
                            $ title = mb_strtolower ($ title, 'UTF-8');
                    }
                    $ title = utf8_uri_encode ($ title, 200);
            }
            $ title = strtolower ($ title);
            $ title = preg numplace ('/&.+?;/', '', $ title); // giết các thực thể
            $ title = preg numplace ('/ [^% a-z0-9 _-] /', '', $ title);
            $ title = preg numplace ('/ \ s + /', '-', $ title);
            $ title = preg numplace ('| - + |', '-', $ title);
            $ title = trim ($ title, '-');
            trả lại tiêu đề $;
    }

Chức năng này cũng như một số chức năng hỗ trợ có thể được tìm thấy trong wp-gộp / format.php.


6
Đây không phải là câu trả lời đầy đủ. Bạn đang thiếu các chức năng như : remove_accents, seems_utf8...
Nikola Loncar

để hoàn thành @The How-To Geek trả lời bạn vẫn có thể git clone git://core.git.wordpress.org/và tìm wp-includes/formatting.phptệp vào
mickro

5

Nếu bạn đang sử dụng Rails edge, bạn có thể dựa vào Inflector.parametrize - đây là ví dụ từ tài liệu:

  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>

Ngoài ra nếu bạn cần xử lý các ký tự kỳ lạ hơn như dấu (éphémère) trong phiên bản Rails trước đó, bạn có thể sử dụng hỗn hợp PermalinkFuDiacriticsFu :

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

DiacriticsFu::escape("räksmörgås")
=> "raksmorgas"

5

Tôi không quen thuộc với Ruby on Rails, nhưng sau đây là mã PHP (chưa được kiểm tra). Bạn có thể dịch nó rất nhanh sang Ruby on Rails nếu bạn thấy nó hữu ích.

$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

Tôi hi vọng cái này giúp được.


4

Tôi không biết nhiều về Ruby hay Rails, nhưng ở Perl, đây là điều tôi sẽ làm:

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

Tôi vừa làm một bài kiểm tra nhanh và nó có vẻ hiệu quả. Hy vọng rằng điều này tương đối dễ dàng để dịch sang Ruby.


4

Triển khai T-SQL, được điều chỉnh từ 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

4

Tôi biết đó là câu hỏi rất cũ nhưng vì hầu hết các trình duyệt hiện đều hỗ trợ các url unicode, tôi đã tìm thấy một giải pháp tuyệt vời trong XRegex chuyển đổi mọi thứ trừ các chữ cái (trong tất cả các ngôn ngữ thành '-').

Điều đó có thể được thực hiện trong một số ngôn ngữ lập trình.

Mẫu này là \\p{^L}+và sau đó bạn chỉ cần sử dụng nó để thay thế tất cả các chữ cái không thành '-'.

Ví dụ hoạt động trong node.js với mô-đun 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"

3

Giả sử rằng lớp mô hình của bạn có thuộc tính tiêu đề, bạn chỉ cần ghi đè phương thức to_param trong mô hình, như sau:

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

Tập Railscast này có tất cả các chi tiết. Bạn cũng có thể đảm bảo rằng tiêu đề chỉ chứa các ký tự hợp lệ bằng cách sử dụng:

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

2

Mã của Brian, trong Ruby:

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

downcasechuyển chuỗi sang chữ thường, striploại bỏ hàng đầu và dấu khoảng trắng, là người đầu tiên gsubgọi g lobally phụ stitutes không gian với dấu gạch ngang, và để loại bỏ việc thứ hai tất cả mọi thứ đó không phải là một ký tự hoặc một dấu gạch ngang.


2

Có một plugin Ruby on Rails nhỏ có tên PermalinkFu , thực hiện điều này. Các phương pháp thoát hiện chuyển đổi thành một chuỗi đó là thích hợp cho một URL . Có một cái nhìn vào mã; phương pháp đó khá đơn giản.

Để xóa các ký tự không phải ASCII, nó sử dụng biểu tượng lib để dịch sang 'ascii // bỏ qua // translit' từ 'utf-8'. Không gian sau đó được biến thành dấu gạch ngang, mọi thứ đều bị giảm, v.v.


Mặc dù điều này hoạt động hoàn hảo, nhưng bằng cách nào đó tôi cảm thấy nó không hiệu quả lắm.
WhyNotHugo

2

Bạn có thể sử dụng phương pháp trợ giúp sau. Nó có thể chuyển đổi các ký tự 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("--","-");
}

2

Đây là phiên bản (chậm, nhưng thú vị để viết) mã của Jeff:

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

Chuỗi thử nghiệm của tôi:

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


2

Các giải pháp stackoverflow là rất tốt, nhưng trình duyệt hiện đại (không bao gồm IE, như thường lệ) bây giờ xử lý mã hóa độc đáo utf8:

nhập mô tả hình ảnh ở đây

Vì vậy, tôi đã nâng cấp giải pháp đề xuất:

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

Mã đầy đủ về Pastebin

Chỉnh sửa: Đây là mã cho RemapInternationalCharToAsciiphương thức (thiếu trong pastebin).


Theo Wikipedia , Mozilla 1.4, Netscape 7.1, Opera 7.11 là một trong những ứng dụng đầu tiên hỗ trợ IDNA. Một plugin trình duyệt có sẵn cho Internet Explorer 6 để cung cấp hỗ trợ IDN. API URL của Internet Explorer 7.0 và Windows Vista cung cấp hỗ trợ riêng cho IDN. Âm thanh như loại bỏ các ký tự UTF-8 là một sự lãng phí thời gian. UTF-8 sống lâu !!!
Muhammad Rehan Saeed

1

Tôi thích cách này được thực hiện mà không sử dụng các biểu thức thông thường , vì vậy tôi đã chuyển nó sang PHP. Tôi vừa thêm một chức năng được gọi is_betweenđể kiểm tra các ký tự:

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

1

Bây giờ tất cả Trình duyệt xử lý mã hóa utf8 độc đáo, vì vậy bạn có thể sử dụng Phương thức WebUtility.UrlEncode , giống như httpUtility.UrlEncode được sử dụng bởi @giamin nhưng nó hoạt động bên ngoài ứng dụng web.


1

Tôi đã chuyển mã sang TypeScript. Nó có thể dễ dàng thích nghi với JavaScript.

Tôi đang thêm một .containsphương thức vào Stringnguyên mẫu, nếu bạn đang nhắm mục tiêu các trình duyệt mới nhất hoặc ES6, bạn có thể sử dụng .includesthay thế.

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

0

Không không không. Bạn là tất cả rất sai. Ngoại trừ các công cụ diacritics-fu, bạn đang đến đó, nhưng còn các nhân vật châu Á (xấu hổ với các nhà phát triển Ruby vì đã không xem xét anh em nihonjin của họ ).

Cả Firefox và Safari đều hiển thị các ký tự không phải ASCII trong URL và thật lòng chúng trông rất tuyệt. Thật tuyệt khi hỗ trợ các liên kết như ' http://somewhere.com/news/read/ お 前 た ち は ア ホ じ い か '.

Vì vậy, đây là một số mã PHP sẽ làm điều đó, nhưng tôi chỉ viết nó và không căng thẳng đã kiểm tra nó.

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

Thí dụ:

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

Đầu ra: コ リ ン -and- ト ー マ ス -and- ア ー ノ ド

'-Và-' là vì & 'được đổi thành' -and- '.


4
Tôi thực sự không biết phải nói gì về thông tin này.
sjas

3
Đó là một ví dụ thực sự tốt khi KHÔNG sử dụng câu lệnh chuyển đổi trường hợp.
NickG
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.