2008-08-24 32 views
228

là gì tốt hoàn regular expression hoặc một số quá trình khác mà có thể mất danh hiệu:Stack Overflow tạo URL thân thiện với SEO của nó như thế nào?

Làm thế nào để bạn thay đổi tiêu đề để trở 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 SEO thân thiện 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 nền tảng cụ thể khác (.NET, PHP, Django), tôi cũng rất thích xem những điều đó.

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

Tôi đang sử dụng 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 được xóa, tất cả đều là chữ thường và tất cả khoảng trắng được thay thế.

+1

Nên được di chuyển đến [meta] (http://meta.stackoverflow.com); như câu hỏi và câu trả lời cả hai cụ thể đối phó với 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ĩ rằng Jeff không được phép có một số danh tiếng không phải là meta không? Câu hỏi đặt ra là "làm thế nào có thể làm một cái gì đó như thế này", không cụ thể "làm thế nào được thực hiện ở đây". –

+0

@ PaŭloEbermann: Nó không phải về Jeff nhận được một số danh tiếng không meta (bao nhiêu danh tiếng ông đã thực sự không phải là mối quan tâm của tôi); cơ thể câu hỏi * đã tham khảo cụ thể việc triển khai StackOverflow * do đó lý do cho nó là trên meta. – casperOne

Trả lời

266

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

Đây là phiên bản thứ hai, chưa được xếp hạng để có hiệu suất gấp 5 lần (và có, tôi đã đánh giá nó). Tôi figured tôi tối ưu hóa nó bởi vì chức năng này có thể được gọi là hàng trăm lầ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 được thay thế (nhưng có chức năng tương đương và 5x nhanh hơn), 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, mã nguồn phương thức RemapInternationalCharToAscii có thể được tìm thấy here.

+21

Nó sẽ là tốt đẹp với một phiên bản mà không chỉ thả ký tự có dấu như åäö nhưng thay vì deaccentuate họ để aao ... ^^ –

+21

@oskar stub của rằng 'RemapInternationalCharToAscii()' chức năng là có http: // meta. stackexchange.com/questions/7435/non-us-ascii-characters-dropped-from-full-profile-url/7696#7696 –

+3

Điều này 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.Length == 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 chuyển. –

16

Bạn sẽ muốn thiết lập tuyến đường 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à một số introduction khi sử dụng công cụ định tuyến của chúng.

Trong Ruby, bạn sẽ cần một biểu hiện thường xuyên như bạn đã biết và đây là biểu hiện thường xuyên sử dụng:

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

Tôi không quen thuộc với Ruby on Rails, nhưng sau đây là (chưa được kiểm tra) PHP mã. Bạn có thể dịch nó rất nhanh chóng 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 hy vọng điều này sẽ hữu ích.

4

tôi không nhiều về Ruby hoặc Rails, nhưng trong Perl, đây là những gì tôi sẽ làm gì:

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 chỉ làm một thử nghiệm nhanh và có vẻ như để làm việc. Hy vọng rằng điều này tương đối dễ dịch sang Ruby.

3

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

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

This Railscast episode 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' 
3

Điều gì về nhân vật hài hước? Bạn sẽ làm gì với chúng? Umlauts? Chấm câu? Những cần phải đượ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ả các ký tự bạn cho phép, ký tự nào bạn sẽ chuyển đổi (thành cái gì?) Và sau đó thay đổi phần còn lại thành 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 nhân vật?

2

đang Brian, trong Ruby:

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

downcase biến chuỗi thành chữ thường, strip loại bỏ hàng đầu và dấu khoảng trắng, các gsub cuộc gọi đầu tiên g lobally phụ stitutes không gian với dấu gạch ngang, và lần thứ hai xóa mọi thứ không phải là chữ cái hoặc dấu gạch ngang.

9

Để đo lường tốt, đây là chức năng PHP trong WordPress thực hiện điều đó ... Tôi nghĩ 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.

 
    function sanitize_title_with_dashes($title) { 
      $title = strip_tags($title); 
      // Preserve escaped octets. 
      $title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title); 
      // Remove percent signs that are not part of an octet. 
      $title = str_replace('%', '', $title); 
      // Restore octets. 
      $title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title); 
      $title = remove_accents($title); 
      if (seems_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); // kill entities 
      $title = preg_replace('/[^%a-z0-9 _-]/', '', $title); 
      $title = preg_replace('/\s+/', '-', $title); 
      $title = preg_replace('|-+|', '-', $title); 
      $title = trim($title, '-'); 
      return $title; 
    } 

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

+5

Đâ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',' seem_utf8' ... –

+0

để 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 tệp 'wp -includes/formats.php' tập tin vào – mickro

2

Có một plugin Ruby on Rails nhỏ gọi là PermalinkFu, thực hiện việc này. escape method biến đổi thành chuỗi phù hợp với URL. Hãy xem mã; phương pháp đó khá đơn giản.

Để xóa không phải ASCII ký tự, nó sử dụng lib biểu tượng để dịch thành 'ascii // ignore // translit' từ 'utf-8'. Không gian này sau đó được chuyển thành dấu gạch ngang, tất cả mọi thứ được downcased vv

+0

Trong khi điều này hoạt động hoàn hảo, tôi bằng cách nào đó cảm thấy nó không phải là rất hiệu quả. – WhyNotHugo

11

Bạn cũng có thể sử dụng JavaScript chức năng này cho thế hệ trong hình thức của sên (cái này được 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 
} 
4

thực hiện T-SQL, chuyển thể 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 
5

Nếu bạn đang sử dụng Rails cạnh, bạn có thể dựa vào Inflector.parametrize - đây là ví dụ từ các 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="/person/1-donald-e-knuth">Donald E. Knuth</a> 

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

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

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

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

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

Vì vậy, đây là một số mã PHP mà sẽ làm điều đó, nhưng tôi chỉ viết nó và không căng thẳng thử nghiệm 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; 
    } 

Ví dụ:

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

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

Các '-and-' được vì & 's có được thay đổi để' -and- '.

+3

Tôi thực sự không biết phải nói gì về mẩu 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 case switch. – NickG

28

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

  • Dấu nối được nối thêm theo cách có thể thêm vào, sau đó cần xóa vì đó là ký tự cuối cùng trong chuỗi. Đó là, chúng tôi không bao giờ muốn "my-slug-". Điều này có nghĩa là một phân bổ chuỗi bổ sung để loại bỏ nó trên trường hợp cạnh này. Tôi đã giải quyết vấn đề này bằng cách trì hoãn sự chậm trễ. Nếu bạn so sánh mã của tôi với logic của Jeff, điều này rất dễ làm theo.
  • Cách tiếp cận của anh ấy hoàn toàn là tìm kiếm dựa trên và bỏ qua rất nhiều ký tự mà tôi đã tìm thấy trong các ví dụ khi nghiên cứu về Stack   Lỗi tràn. Để chống lại điều này, đầu tiên tôi tạo một đường chuẩn hóa (đối chiếu AKA được đề cập trong câu hỏi tràn Meta Stack Non US-ASCII characters dropped from full (profile) URL), sau đó bỏ qua bất kỳ ký tự nào ngoài phạm vi có thể chấp nhận được. Điều này hoạt động hầu hết thời gian ...
  • ... Vì khi nào tôi cũng 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 ngoại lệ thủ công mà không nghi ngờ có đầy đủ các lỗ hổng, nhưng nó tốt hơn là không có gì. Mã bình thường đượ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 How can I remove accents on a string?.
  • Chuyển đổi trường hợp giờ đây 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 
        /// https://meta.stackexchange.com/questions/7435/non-us-ascii-characters-dropped-from-full-profile-url/7696#7696 
        /// https://stackoverflow.com/questions/25259/how-do-you-include-a-webpage-title-as-part-of-a-webpage-url/25486#25486 
        /// https://stackoverflow.com/questions/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, các bài kiểm tra đơn vị, và giải thích lý do tại sao Facebook 's URL chương trình là một chút thông minh hơn stack tràn, tôi đã có một expanded version of this on my blog.

+3

+1 Điều này thật tuyệt vời. Tôi cũng đã thêm một bình luận trên blog của bạn về việc có thể thay đổi 'if (i == maxlen) break;' thành 'if (sb.Length == maxlen) break;' thay vào đó nếu bạn truyền vào một chuỗi với rất nhiều khoảng trống/các ký tự không hợp lệ bạn vẫn có thể lấy được độ dài mong muốn, trong khi mã như nó có thể kết thúc bằng cách cắt xén nó (ví dụ 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 thô của 10.000.000 lần lặp lại với mã của Jeff cho thấy nó có cùng tốc độ. –

+0

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 bằng với Jeff. – DanH

+0

Thật tuyệt. Cảm ơn Dan! –

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("--","-"); 
} 
1

Tôi thích cách này được thực hiện mà không cần sử dụng regular expressions, vì vậy tôi đã chuyển nó sang PHP.Tôi chỉ cần thêm một chức năng gọi là is_between để kiểm tra nhân vậ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; 
    } 
} 
2

Các stackoverflow solution 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:

enter image description here

Vì vậy, tôi đã nâng cấp giải pháp được đề 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)); 
      } 
    ... 
} 

Full Code on Pastebin

Chỉnh sửa: Here's the code cho phương pháp RemapInternationalCharToAscii (bị thiếu trong pastebin).

+0

Theo [Wikipedia] (http://en.wikipedia.org/wiki/Internationalized_domain_name), Mozilla 1.4, Netscape 7.1, Opera 7.11 nằm trong số các ứng dụng đầu tiên hỗ trợ IDNA. 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. Dài sống UTF-8 !!! –

2

Dưới đây là của tôi (chậm hơn, nhưng thú vị để viết) phiên bản 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... "

1

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

3

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 nay hỗ trợ unicode url Tôi tìm thấy một giải pháp tuyệt vời trong XRegex có thể chuyển đổi tất cả mọi thứ ngoại trừ chữ cái (trong tất cả các ngôn ngữ để '-').

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

Mẫu 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" 
Các vấn đề liên quan