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


253

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

您如何将标题更改为URL的一部分,例如Stack Overflow?

然后变成

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

Stack Overflow上的SEO友好URL中使用了什么?

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

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

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


那有趣的角色呢?你打算怎么办?乌姆劳?标点?这些需要考虑。基本上,我将使用白名单方法,而不是上面的黑名单方法:描述您将允许哪些字符,将哪些字符转换(转换成什么?),然后将其余字符更改为有意义的东西(“”) 。我怀疑您是否可以在一个正则表达式中执行此操作?为什么不仅仅遍历字符?
Daren Thomas

1
应该迁移到meta ; 因为问题和答案都专门涉及SO实现,并且可接受的答案来自@JeffAtwood。
casperOne

19
@casperOne您是否认为Jeff不允许使用某些非元信誉?问题是“如何做这样的事情”,而不是“在这里怎么做”。
圣保罗Ebermann

@PaŭloEbermann:这与Jeff获得非元信誉无关(他拥有多少信誉确实不是我关心的问题);问题主体专门引用了StackOverflow的实现,因此其基本原理基于meta。
casperOne

Answers:


300

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

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

/// <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方法源代码可以在此处找到。


24
这将是一个不错的版本,该版本不仅会丢弃åäö等重音字符,而是将它们重音化为aao ... ^^
Oskar Duveborn

22
@oskar该RemapInternationalCharToAscii()函数的存根位于meta.stackexchange.com/questions/7435/…–
Jeff Atwood

3
这很棒。到目前为止,我所做的唯一更改就是更改了“ if(i == maxlen)break;”。变成“如果(sb.Length == maxlen)中断;” 万一有大量的无效字符我传入的字符串中。
汤姆卓乐

4
次要的优化:if (prevdash) sb.Length -= 1; return sb.ToString();而不是最后的if声明。
Mark Hurd

8
sb.Length == maxlen break;如果maxLenght-1上的符号为“ß”,则@Dommer 会出现错误,sb.Length == maxlene它将永远转换为“ ss” ,因此最好进行测试(sb.Length > = maxlen)
HenrikStenbæk2012年

32

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

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

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

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


4
+1这是伟大的丹。我还在您的博客上添加了一条评论,内容可能是更改if (i == maxlen) break;为,if (sb.Length == maxlen) break;这样,如果您传递的字符串中包含很多空格/无效字符,您仍然可以得到所需长度的段,而最终的代码可能会终止大量截断它(例如,考虑从80个空格开始的情况...)。根据Jeff的代码进行的1000万次迭代的粗略基准测试表明,其速度大致相同。
汤姆·钱特勒

1
谢谢,在我的博客上回复了他,并修复了他在上面和上面的代码。也感谢对​​代码进行基准测试。对于那些感兴趣的人,它可以与杰夫的相提并论。
DanH 2012年

2
Slug.Create()似乎有一些问题:ÆØÅ的大写版本未正确转换Ø在Å转换为a时被忽略。通常,您会将“å”转换为“ aa”,将“ø”转换为“ oe”,将“æ”转换为“ ae”。第二次(sb.Length == maxlen)休息;如果maxLenght-1上的符号为“ß”(sb.Length == maxlen)永远不会为真,则最好进行测试(sb.Length> = maxlen)。我抑制了您削减任何随机位置而不削减最后一个“-”,这将使您免于以结尾不想要的词结尾:好像您必须在最后一个“ s”之后削减“断言” “
HenrikStenbæk2012年

@DanH,最好使用javascript版本的代码。
Freshblood 2014年

16

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

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

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

11

您还可以使用此JavaScript函数以形式形式生成子弹(该子弹基于/从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
}

添加一些let或const会很棒,因为这不是普通的JS。
阿迪亚·阿南德

8

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

    函数sanitize_title_with_dashes($ title){
            $ title = strip_tags($ title);
            //保留转义的八位位组。
            $ title = preg_replace('|%([a-fA-F0-9] [a-fA-F0-9])|','--- $ 1 ---',$ title);
            //删除不属于八位字节的百分号。
            $ title = str_replace('%','',$ title);
            //恢复八位位组。
            $ title = preg_replace('| ---([[a-fA-F0-9] [a-fA-F0-9])--- |','%$ 1',$ title);
            $ title = remove_accents($ title);
            如果(seems_utf8($ title)){
                    如果(function_exists('mb_strtolower')){
                            $ title = mb_strtolower($ title,'UTF-8');
                    }
                    $ title = utf8_uri_encode($ title,200);
            }
            $ title = strtolower($ title);
            $ title = preg_replace('/&.+?;/','',$ title); //杀死实体
            $ title = preg_replace('/ [^%a-z0-9 _-] /','',$ title);
            $ title = preg_replace('/ \ s + /','-',$ title);
            $ title = preg_replace('|-+ |','-',$ title);
            $ title = trim($ title,'-');
            返回$ title;
    }

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


6
这不是完整的答案。您缺少以下功能:remove_accentsseems_utf8...
Nikola Loncar,2014年

完成@The How-To Geek答案,您仍然可以git clone git://core.git.wordpress.org/找到wp-includes/formatting.php文件,并将其放入
mickro

5

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

  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>

另外,如果您需要处理更多异国情调的字符,例如在先前版本的Rails中使用重音符号(éphémère),则可以混合使用PermalinkFuDiacriticsFu

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

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

5

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

$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

我希望这有帮助。


4

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

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

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


4

T-SQL实现,改编自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

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

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

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

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

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

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

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

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

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

2

Brian的Ruby代码:

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

downcase原来的字符串为小写,strip除去开头和结尾的空白,第一个gsub呼叫 lobally 破折号stitutes空间,而第二个方法删除一切,不是一个字母或破折号。


2

有一个名为PermalinkFu的小型Ruby on Rails插件可以做到这一点。该逃生方法确实转变成一个字符串,它是适用于URL。看一下代码;该方法非常简单。

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


虽然这可以正常工作,但我不知何故效率不高。
WhyNotHugo 2012年

2

您可以使用以下帮助方法。它可以转换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

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

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


2

计算器的解决方案是伟大的,但现代的浏览器(IE除外,像往常一样),现在很好的处理utf8编码:

在此处输入图片说明

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

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方法(这是在缺少引擎收录)。


根据Wikipedia的介绍,Mozilla 1.4,Netscape 7.1,Opera 7.11是最早支持IDNA的应用程序。Internet Explorer 6提供了一个浏览器插件来提供IDN支持。Internet Explorer 7.0和Windows Vista的URL API提供了对IDN的本机支持。听起来像删除UTF-8字符是浪费时间。UTF-8万岁!!!
穆罕默德·雷汉

1

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

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

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


1

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

.containsString原型中添加了一种方法,如果您的目标是最新的浏览器或ES6,则可以.includes改用。

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

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

Firefox和Safari都在URL中显示非ASCII字符,坦率地说,它们看起来很棒。很高兴支持“ http://somewhere.com/news/read/お前おアホじゃないかい ”之类的链接。

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

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

例:

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

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

“与”是因为&更改为“与”。


4
我真的不知道该说些什么。
sjas 2012年

3
这是何时不使用switch case语句的一个很好的例子。
NickG
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.