【问题标题】:How can I create a SEO friendly dash-delimited url from a string?如何从字符串创建 SEO 友好的破折号分隔 url?
【发布时间】:2010-10-02 17:07:49
【问题描述】:

取一个字符串如:

在 C# 中:如何在以逗号分隔的字符串列表中的字符串周围添加“引号”?

并将其转换为:

in-c-how-do-i-add-quotes-around-string-in-a-comma-delimited-list-of-strings

要求:

  • 用破折号分隔每个单词并删除所有标点符号(考虑到并非所有单词都用空格分隔。)
  • 函数接受最大长度,并获取低于该最大长度的所有标记。示例:ToSeoFriendly("hello world hello world", 14) 返回"hello-world"
  • 所有单词都转换为小写。

另外,是否应该有最小长度?

【问题讨论】:

  • 这里有一些关于 url 长度的信息:boutell.com/newfaq/misc/urllength.html
  • 也许用他们的“英语发音”替换一些特殊字符,例如"#" => "sharp",将允许制作更好的 url 并将 C 与 C# 区分开来(这很好,对;)?)?
  • 是的,当然,除了 # 不尖锐,那是一个不同的符号 ;p
  • 对...但我想你明白我的意思;)...

标签: language-agnostic string seo slug


【解决方案1】:

为此,我们需要:

  1. 规范化文本
  2. 删除所有变音符号
  3. 替换国际字符
  4. 能够缩短文本以匹配 SEO 阈值

我想要一个函数来生成整个字符串并输入一个可能的最大长度,这就是结果。

public static class StringHelper
{
/// <summary>
/// Creates a URL And SEO friendly slug
/// </summary>
/// <param name="text">Text to slugify</param>
/// <param name="maxLength">Max length of slug</param>
/// <returns>URL and SEO friendly string</returns>
public static string UrlFriendly(string text, int maxLength = 0)
{
    // Return empty value if text is null
    if (text == null) return "";

    var normalizedString = text
        // Make lowercase
        .ToLowerInvariant()
        // Normalize the text
        .Normalize(NormalizationForm.FormD);

    var stringBuilder = new StringBuilder();
    var stringLength = normalizedString.Length;
    var prevdash = false;
    var trueLength = 0;

    char c;

    for (int i = 0; i < stringLength; i++)
    {
        c = normalizedString[i];

        switch (CharUnicodeInfo.GetUnicodeCategory(c))
        {
            // Check if the character is a letter or a digit if the character is a
            // international character remap it to an ascii valid character
            case UnicodeCategory.LowercaseLetter:
            case UnicodeCategory.UppercaseLetter:
            case UnicodeCategory.DecimalDigitNumber:
                if (c < 128)
                    stringBuilder.Append(c);
                else
                    stringBuilder.Append(ConstHelper.RemapInternationalCharToAscii(c));

                prevdash = false;
                trueLength = stringBuilder.Length;
                break;

            // Check if the character is to be replaced by a hyphen but only if the last character wasn't
            case UnicodeCategory.SpaceSeparator:
            case UnicodeCategory.ConnectorPunctuation:
            case UnicodeCategory.DashPunctuation:
            case UnicodeCategory.OtherPunctuation:
            case UnicodeCategory.MathSymbol:
                if (!prevdash)
                {
                    stringBuilder.Append('-');
                    prevdash = true;
                    trueLength = stringBuilder.Length;
                }
                break;
        }

        // If we are at max length, stop parsing
        if (maxLength > 0 && trueLength >= maxLength)
            break;
    }

    // Trim excess hyphens
    var result = stringBuilder.ToString().Trim('-');

    // Remove any excess character to meet maxlength criteria
    return maxLength <= 0 || result.Length <= maxLength ? result : result.Substring(0, maxLength);
}
}

此助手用于将一些国际字符重新映射为可读字符。

public static class ConstHelper
{
/// <summary>
/// Remaps international characters to ascii compatible ones
/// based of: https://meta.stackexchange.com/questions/7435/non-us-ascii-characters-dropped-from-full-profile-url/7696#7696
/// </summary>
/// <param name="c">Charcter to remap</param>
/// <returns>Remapped character</returns>
public static string RemapInternationalCharToAscii(char c)
{
    string s = c.ToString().ToLowerInvariant();
    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 "";
    }
}
}

函数会像这样工作

const string text = "ICH MUß EINIGE CRÈME BRÛLÉE HABEN";
Console.WriteLine(StringHelper.URLFriendly(text));
// Output: 
// ich-muss-einige-creme-brulee-haben

这个问题已经回答了很多次here,但没有一个优化过。 您可以通过一些示例找到整个源代码here on github。 您可以从Johan Boström's Blog 阅读更多内容。有关这方面的更多信息与 .NET 4.5+ 和 .NET Core 兼容。

【讨论】:

    【解决方案2】:

    这与 Stack Overflow 生成 slug 的方式很接近:

    public static string GenerateSlug(string title)
    {
        string slug = title.ToLower();
        if (slug.Length > 81)
          slug = slug.Substring(0, 81);
        slug = Regex.Replace(slug, @"[^a-z0-9\-_\./\\ ]+", "");
        slug = Regex.Replace(slug, @"[^a-z0-9]+", "-");
    
        if (slug[slug.Length - 1] == '-')
          slug = slug.Remove(slug.Length - 1, 1);
        return slug;
    }
    

    【讨论】:

      【解决方案3】:

      在python中,(如果安装了django,即使你正在使用另一个框架。)

      from django.template.defaultfilters import slugify
      slugify("In C#: How do I add "Quotes" around string in a comma delimited list of strings?")
      

      【讨论】:

        【解决方案4】:

        另一个季节,另一个选择 Ruby 的原因 :)

        def seo_friendly(str)
          str.strip.downcase.gsub /\W+/, '-'
        end
        

        就是这样。

        【讨论】:

        • 那是 Rails 的,不是普通的 Ruby 的。
        【解决方案5】:

        shell 中的解决方案:

        echo 'In C#: How do I add "Quotes" around string in a comma delimited list of strings?' | \
            tr A-Z a-z | \
            sed 's/[^a-z0-9]\+/-/g;s/^\(.\{1,20\}\).*/\1/'
        

        【讨论】:

          【解决方案6】:

          Perl 中的解决方案:

          my $input = 'In C#: How do I add "Quotes" around string in a comma delimited list of strings?';
          
          my $length = 20;
          $input =~ s/[^a-z0-9]+/-/gi;
          $input =~ s/^(.{1,$length}).*/\L$1/;
          
          print "$input\n";
          

          完成。

          【讨论】:

            【解决方案7】:

            更好的版本:

            function Slugify($string)
            {
                return strtolower(trim(preg_replace(array('~[^0-9a-z]~i', '~-+~'), '-', $string), '-'));
            }
            

            【讨论】:

              【解决方案8】:

              至少在 PHP 中执行此操作的一种更简洁的方法是:

              function CleanForUrl($urlPart, $maxLength = null) {
                  $url = strtolower(preg_replace(array('/[^a-z0-9\- ]/i', '/[ \-]+/'), array('', '-'), trim($urlPart)));
                  if ($maxLength) $url = substr($url, 0, $maxLength);
                  return $url;
              }
              

              最好在开始时使用trim(),这样以后处理的事情就更少了,完全替换在preg_replace() 中完成。

              感谢 cg 提出大部分内容:What is the best way to clean a string for placement in a URL, like the question name on SO?

              【讨论】:

                【解决方案9】:

                这是我在 C# 中的解决方案

                private string ToSeoFriendly(string title, int maxLength) {
                    var match = Regex.Match(title.ToLower(), "[\\w]+");
                    StringBuilder result = new StringBuilder("");
                    bool maxLengthHit = false;
                    while (match.Success && !maxLengthHit) {
                        if (result.Length + match.Value.Length <= maxLength) {
                            result.Append(match.Value + "-");
                        } else {
                            maxLengthHit = true;
                            // Handle a situation where there is only one word and it is greater than the max length.
                            if (result.Length == 0) result.Append(match.Value.Substring(0, maxLength));
                        }
                        match = match.NextMatch();
                    }
                    // Remove trailing '-'
                    if (result[result.Length - 1] == '-') result.Remove(result.Length - 1, 1);
                    return result.ToString();
                }
                

                【讨论】:

                  【解决方案10】:

                  C#

                  public string toFriendly(string subject)
                  {
                      subject = subject.Trim().ToLower();
                      subject = Regex.Replace(subject, @"\s+", "-");
                      subject = Regex.Replace(subject, @"[^A-Za-z0-9_-]", "");
                      return subject;
                  }
                  

                  【讨论】:

                  • 我认为这有一些问题,因为这种情况怎么办:(string)someObject 失败。变为:stringsomeobject-fails
                  • 这确实发生在我身上,但坦率地说,我不确定我想如何处理它。在过去,我已经对括号之间的所有内容进行了核对,包括括号,但我怀疑它是特定于实现的。不管你想要什么,添加到上面的模板中都是微不足道的。
                  【解决方案11】:

                  我会按照以下步骤操作:

                  1. 将字符串转换为小写
                  2. 用连字符替换不需要的字符
                  3. 用一个连字符替换多个连字符 (没有必要,因为preg_replace() 函数调用已经阻止了多个连字符)
                  4. 如有必要,删除开头和结尾的连字符
                  5. 如果需要,从位置 x 之前的最后一个连字符修剪到末尾

                  所以,都在一个函数中(PHP):

                  function generateUrlSlug($string, $maxlen=0)
                  {
                      $string = trim(preg_replace('/[^a-z0-9]+/', '-', strtolower($string)), '-');
                      if ($maxlen && strlen($string) > $maxlen) {
                          $string = substr($string, 0, $maxlen);
                          $pos = strrpos($string, '-');
                          if ($pos > 0) {
                              $string = substr($string, 0, $pos);
                          }
                      }
                      return $string;
                  }
                  

                  【讨论】:

                  • 我喜欢这个解决方案。我试图通过匹配所有非字母数字并将它们拆分加入来做到这一点 - 我一直在尝试匹配,只有当它们不是字符串的开头或结尾,但从未让它工作。最后,我决定匹配单词并附加。
                  • 如果第一个单词超过最大长度怎么办?
                  • 在那种情况下我返回了一个子字符串
                  【解决方案12】:

                  这里有一个php的解决方案:

                  function make_uri($input, $max_length) {
                    if (function_exists('iconv')) {  
                      $input = @iconv('UTF-8', 'ASCII//TRANSLIT', $input);  
                    }
                  
                    $lower = strtolower($input);
                  
                  
                    $without_special = preg_replace_all('/[^a-z0-9 ]/', '', $input);
                    $tokens = preg_split('/ +/', $without_special);
                  
                    $result = '';
                  
                    for ($tokens as $token) {
                      if (strlen($result.'-'.$token) > $max_length+1) {
                        break;
                      }
                  
                      $result .= '-'.$token;       
                    }
                  
                    return substr($result, 1);
                  }
                  

                  用法:

                  echo make_uri('In C#: How do I add "Quotes" around string in a ...', 500);
                  

                  除非您需要可键入的 uri,否则它们不需要很小。但是您应该指定一个最大值,以便 url 与代理等很好地配合使用。

                  【讨论】:

                    猜你喜欢
                    • 2011-12-12
                    • 1970-01-01
                    • 2014-07-14
                    • 1970-01-01
                    • 2016-11-16
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-09-29
                    相关资源
                    最近更新 更多