Stack Overflow如何生成其SEO友好的URL?

How does Stack Overflow generate its SEO-friendly URLs?

什么是一个好的完整正则表达式或其他带有标题的过程:

How do you change a title to be part of the URL like Stack Overflow?

然后变成

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

在Stack上的SEO友好URL中使用的;溢出?

我正在使用的开发环境是Ruby on Rails,但是如果还有其他特定于平台的解决方案(.NET,PHP,Django),我也希望看到这些。

我确信我(或其他读者)会在不同的平台上遇到相同的问题。

我正在使用自定义路由,我主要想知道如何将字符串更改为删除所有特殊字符,全部为小写字母并替换所有空白。


这是我们的方法。请注意,边缘条件可能比您乍看之下要多。

这是第二个版本,其性能提高了5倍(是的,我对其进行了基准测试)。我认为我会对其进行优化,因为该功能每页可以调用数百次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/// <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();
}

要查看被其替换的代码的先前版本(但功能等效,且速度快5倍),请查看此帖子的修订历史记录(单击日期链接)。

另外,可以在这里找到RemapInternationalCharToAscii方法的源代码。


这是我的Jeff代码版本。我进行了以下更改:

  • 连字符以可以添加的方式添加,然后需要删除,因为它是字符串中的最后一个字符。也就是说,我们永远不要"我的-"。这意味着需要额外的字符串分配以在这种情况下将其删除。我通过延迟连字符来解决此问题。如果将我的代码与Jeff的代码进行比较,则很容易理解。
  • 他的方法是完全基于查找的,并且错过了我在研究StackOverflow时在示例中发现的许多字符。为了解决这个问题,我首先执行规范化过程(" Meta Stack溢出"问题中提到的AKA归类,从完整(配置文件)URL中删除了非US-ASCII字符),然后忽略了超出可接受范围的任何字符。这在大多数情况下都有效...
  • ...如果不是这样,我还必须添加一个查找表。如上所述,标准化时某些字符不会映射为低ASCII值。除了得到这些例外情况之外,我没有手动列出例外情况,这些例外情况无疑充满了漏洞,但是总比没有好。规范化代码的灵感来自于乔恩·汉娜(Jon Hanna)在Stack Overflow问题中的精彩文章,我该如何删除字符串的重音符号?
  • 现在,大小写转换也是可选的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    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 'T':
                    swap ="th";
                    break;
            }
            return swap;
        }
    }

有关更多详细信息,单元测试以及为什么Facebook的URL方案比Stack Overflows聪明一些的解释,我在博客上有了此扩展版。


您将需要设置一个自定义路由,以将URL指向将处理该URL的控制器。由于您使用的是Ruby on Rails,因此这里是使用其路由引擎的介绍。

在Ruby中,您将需要一个已经知道的正则表达式,这是要使用的正则表达式:

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

您还可以使用此JavaScript函数以形式形式生成子弹(该子弹基于/从Django复制):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
}


从好的方面来说,这是WordPress中的PHP函数实现的...我想WordPress是使用花哨链接的较流行平台之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    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;
    }

此功能以及某些支持功能可以在wp-includes / formatting.php中找到。


我不熟悉Ruby on Rails,但以下是(未经测试的)PHP代码。如果发现有用,可以将其快速转换为Ruby on Rails。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$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

我希望这有帮助。


如果您使用的是Rails edge,则可以依赖Inflector.parametrize-这是文档中的示例:

1
2
3
4
5
6
7
8
9
10
11
  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)) %>
  # => Donald E. Knuth

另外,如果您需要处理更早版本的Rails中的异国字符,例如重音符号(éphémère),则可以混合使用PermalinkFu和DiacriticsFu:

1
2
3
4
5
DiacriticsFu::escape("éphémère")
=>"ephemere"

DiacriticsFu::escape("r?ksm?rg?s")
=>"raksmorgas"

我对Ruby或Rails的了解不多,但是在Perl中,我会这样做:

1
2
3
4
5
6
7
8
9
10
11
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
$url
";

我只是做了一个快速测试,它似乎可以工作。希望这相对容易翻译成Ruby。


T-SQL实现,改编自dbo.UrlEncode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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


假设您的模型类具有title属性,则可以简单地在模型中覆盖to_param方法,如下所示:

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

此Railscast情节包含所有详细信息。您还可以使用以下方法确保标题仅包含有效字符:

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

我知道这是一个非常老的问题,但是由于大多数浏览器现在都支持unicode url,所以我在XRegex中找到了一个很好的解决方案,该解决方案可以将除字母之外的所有内容(所有语言都转换为"-")转换。

这可以用几种编程语言来完成。

模式为\\p{^L}+,然后只需使用它即可将所有非字母替换为'-'。

使用xregex模块的node.js中的工作示例。

1
2
3
4
5
6
7
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"

您可以使用以下帮助方法。它可以转换Unicode字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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("--","-");
}

stackoverflow解决方案很棒,但是现代的浏览器(通常不包括IE)现在可以很好地处理utf8编码:

enter image description here

因此,我升级了建议的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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));
            }
    ...
}

Pastebin的完整代码

编辑:这是RemapInternationalCharToAscii方法的代码(pastebin中缺少该代码)。


有一个名为PermalinkFu的小型Ruby on Rails插件可以做到这一点。转义方法将转换为适合URL的字符串。看一下代码;该方法非常简单。

要删除非ASCII字符,它使用iconv库将其从'utf-8'转换为'ascii // ignore // translit'。然后将空格变成破折号,将所有内容缩小写,等等。


这是我的(较慢,但很有趣的)杰夫代码版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);
}

我的测试字符串:

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


Brian的Ruby代码:

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

downcase将字符串转换为小写字母,strip删除前导和尾随空格,第一个gsub调用全局地将空格替换为破折号,第二个调用则去除所有不是字母或破折号的空格。


我喜欢不使用正则表达式的方式,因此将其移植到PHP。我刚刚添加了一个名为is_between的函数来检查字符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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???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('y?', $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('T', $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]) ."

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

现在,所有浏览器都可以很好地处理utf8编码,因此您可以使用WebUtility.UrlEncode方法,类似于@giamin使用的HttpUtility.UrlEncode,但它的工作在Web应用程序之外。


不不不。你们都错了。除了变音符之类的东西之外,您到那里去了,但是亚洲字符呢(Ruby开发人员因为不考虑其nihonjin兄弟而感到羞耻)。

Firefox和Safari都在URL中显示非ASCII字符,坦率地说,它们看起来很棒。支持" http://somewhere.com/news/read/お前たちはアホじゃないかい"之类的链接真是太好了。

因此,这里有一些PHP代码可以做到这一点,但是我只是编写了它,而没有对其进行压力测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<?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;
    }

例:

1
2
3
4
5
6
7
8
9
10
$str ="~!@#$%^&*()_+-=[]\{}|;':",./<>?

\t\x07\x00\x04 コリン ~!@#$%^&*()_+-=[]\{}|;':",./<>?

\t\x07\x00\x04 トーマス ~!@#$%^&*()_+-=[]\{}|;':",./<>?

\t\x07\x00\x04 アーノルド ~!@#$%^&*()_+-=[]\{}|;':",./<>?

\t\x07\x00\x04";
echo slug($str);

输出:
コリン - 和 - トーマス - 和 - アーノルド

"与"是因为&更改为"与"。


我将代码移植到TypeScript。它可以轻松地适应JavaScript。

我在String原型中添加了.contains方法,如果您的目标是最新的浏览器或ES6,则可以改用.includes

[cc] if(!String.prototype.contains){
String.prototype.contains =函数(检查){
返回this.indexOf(check,0)!== -1;
};
}

声明接口字符串{
contains(check:string):布尔值;
}

导出函数MakeUrlFriendly(title:string){
如果(title == null || title =='')
返回'';

const maxlen = 80;
让len = title.length;
让prevdash = false;
让结果='';
让c:字符串;
let cc:number;
让remapInternationalCharToAscii =函数(c:字符串){
令s = c.toLowerCase();
如果("à?áa????" ..包含){
返回" a";
}
else if("èéê??" ..包含){
返回" e";
}
否则,如果("ìí???" ..包含){
返回" i";
}
否则,如果("" ??????? e"。包含){
返回" o";
}
否则,如果("ùú?ü??" ..包含){
返回" u";
}
否则,如果(" ????" ..包含){
返回" c";
}
否则,如果(" ???"。包含){
返回" z";
}
否则,如果(" ????" ..包含){
返回" s";
}
否则if(" ??"。包含)
n返回"
?
如果包含" y",则为n。
n返回"
?
否,是否包含"?"。
n返回"
?
否(c =='?')
n返回"
?
否(c =='?')
n返回"
?
否(c =='?')
n返回"
?
否(c =='?')
n返回" s"
?
否(c =='T')
n返回" t"
?
否(c =='?')
n返回"
?
否(c =='?')
n返回"
?
别的
n返回"
?
n}

n为(让i = 0; i; lt; len; i ++)
n c =标题[i]
n cc = c.charCodeAt(0)

n if((cc; gt; = 97 / * a * / ;; cc; lt; = 122 / * z * /;)||(cc; gt; = 48 / * 0 * / ;; cc; lt; = 57 / * 9 * /))
n结果+ = c
n prevdash =假
?
否,如果((cc; gt; = 65 ;; cc; lt; = 90 / * A-Z * /))
n结果+ = c.toLowerCase()
n prevdash =假
?
如果(c ==''|| c ==','|| c =='。'|| c =='/'|| c =='\'|| c =='-' || c =='_'|| c =='=')
n if(!prevdash ;; result.length; gt; 0)
n结果+ ='-'
n prevdash = true
?
?
否,如果(cc; gt; = 128)
n让prevlen = result.length
n结果+ = remapInternationalCharToAscii(c)
n如果(prevlen!= result.length)prevdash = false
?
n如果(i == maxlen)中断
?

n if(上一个
n返回result.substring(0,result.length-1)
内尔斯
n返回结果
n} <; / code&gt <; / pr>
?


推荐阅读