代码之家  ›  专栏  ›  技术社区  ›  chakrit Dutchie432

C中的URL段塞算法

  •  71
  • chakrit Dutchie432  · 技术社区  · 16 年前

    所以我搜索并浏览了 slug 贴上标签,只找到两个令人信服的解决方案:

    这只是部分解决问题的方法。我可以自己手动编写代码,但我很惊讶目前还没有解决方案。

    那么,C和/或.NET中是否有一个sligify alrogithm实现,能够正确地处理拉丁字符、Unicode和各种其他语言问题?

    5 回复  |  直到 8 年前
        1
  •  141
  •   Cory House    13 年前

    http://predicatet.blogspot.com/2009/04/improved-c-slug-generator-or-how-to.html

    public static string GenerateSlug(this string phrase) 
    { 
        string str = phrase.RemoveAccent().ToLower(); 
        // invalid chars           
        str = Regex.Replace(str, @"[^a-z0-9\s-]", ""); 
        // convert multiple spaces into one space   
        str = Regex.Replace(str, @"\s+", " ").Trim(); 
        // cut and trim 
        str = str.Substring(0, str.Length <= 45 ? str.Length : 45).Trim();   
        str = Regex.Replace(str, @"\s", "-"); // hyphens   
        return str; 
    } 
    
    public static string RemoveAccent(this string txt) 
    { 
        byte[] bytes = System.Text.Encoding.GetEncoding("Cyrillic").GetBytes(txt); 
        return System.Text.Encoding.ASCII.GetString(bytes); 
    }
    
        2
  •  17
  •   Joan Caron    8 年前

    在这里,您可以找到一种在C_中生成URL段塞的方法。此函数用于删除所有重音符号(Marcel的答案)、替换空格、删除无效字符、从结尾处删除短划线并替换“—”或“u”的重复出现项。

    代码:

    public static string ToUrlSlug(string value){
    
            //First to lower case
            value = value.ToLowerInvariant();
    
            //Remove all accents
            var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(value);
            value = Encoding.ASCII.GetString(bytes);
    
            //Replace spaces
            value = Regex.Replace(value, @"\s", "-", RegexOptions.Compiled);
    
            //Remove invalid chars
            value = Regex.Replace(value, @"[^a-z0-9\s-_]", "",RegexOptions.Compiled);
    
            //Trim dashes from end
            value = value.Trim('-', '_');
    
            //Replace double occurences of - or _
            value = Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled);
    
            return value ;
        }
    
        3
  •  11
  •   Community Mohan Dere    9 年前

    这是我的演奏,根据琼和马塞尔的答案。我所做的更改如下:

    代码如下:

    公共类URLSlugger { //空白,em短划线,en短划线,下划线 static readonly regex wordsdelimiters=new regex(@“[\s__“u]”,regexopions.compiled); //无效字符 static readonly regex invalidchars=new regex(@“[^a-z0-9 \-]”,regexopions.compiled); //多个连字符 static readonly regex multiplehyphens=new regex(@“-2,”,regexoptions.compiled); 公共静态字符串toulsllug(字符串值) { //转换为小写 value=value.tolowerinvariant(); //删除音调符号(重音符号) 值=删除评论(值); //确保所有单词分隔符都是连字符 value=wordDelimiters.replace(value,“-”); //删除无效字符 value=invalidChars.replace(value,“”); //用单个连字符替换多个连字符(-) value=multiplehyphens.replace(value,“-”); //从末端修剪连字符(-) 返回值。trim(“-”); } ///参见:http://www.siao2.com/2007/05/14/2629747.aspx 私有静态字符串removediacredits(string stin) { 字符串stformd=stin.normalize(normalizationform.formd); stringbuilder sb=new stringbuilder(); 对于(int ich=0;ich<stformd.length;ich++) { unicodecategory uc=charunicodeinfo.getunicodecategory(stformd[ich]); 如果(UC)!=unicodeCategory.nonspacingMark) { 附加(stformd[ich]); } } return(sb.toString().normalize(normalizationForm.formc)); } } < /代码>

    这仍然不能解决非拉丁字符问题。完全替代的解决方案是使用 uri.escapedatastring 将字符串转换为十六进制表示:

    string original=“_礿_獣___”
    
    //%E6%b5%8b%e8%af%95%e5%85%ac%e5%8f%b8
    转换的字符串=uri.escapedataString(原始);
    < /代码> 
    
    

    然后使用数据生成超链接:

    <a href=“http://www.example.com/100/%e6%b5%8b%e8%af%95%e5%85%ac%e5%8f%b8”>
    __ _、__
    & lt;/a & gt;
    < /代码> 
    
    

    许多浏览器会在地址栏中显示中文字符(见下文),但基于我有限的测试,它不完全受支持。

    注意:为了使uri.escapedatastring以这种方式工作,iriparsing必须启用。


    编辑

    对于那些希望在C_中生成URL段塞的用户,我建议查看以下相关问题:

    stack overflow如何生成其SEO友好的URL?

    这就是我最终用于我的项目的内容。

    跟随:

    • 使用Awidely accepted删除重音符号的方法。
    • 显式regex缓存可适度提高速度。
    • 更多的字分隔符被识别并规范化为连字符。

    代码如下:

    public class UrlSlugger
    {
        // white space, em-dash, en-dash, underscore
        static readonly Regex WordDelimiters = new Regex(@"[\s—–_]", RegexOptions.Compiled);
    
        // characters that are not valid
        static readonly Regex InvalidChars = new Regex(@"[^a-z0-9\-]", RegexOptions.Compiled);
    
        // multiple hyphens
        static readonly Regex MultipleHyphens = new Regex(@"-{2,}", RegexOptions.Compiled);
    
        public static string ToUrlSlug(string value)
        {
            // convert to lower case
            value = value.ToLowerInvariant();
    
            // remove diacritics (accents)
            value = RemoveDiacritics(value);
    
            // ensure all word delimiters are hyphens
            value = WordDelimiters.Replace(value, "-");
    
            // strip out invalid characters
            value = InvalidChars.Replace(value, "");
    
            // replace multiple hyphens (-) with a single hyphen
            value = MultipleHyphens.Replace(value, "-");
    
            // trim hyphens (-) from ends
            return value.Trim('-');
        }
    
        /// See: http://www.siao2.com/2007/05/14/2629747.aspx
        private static string RemoveDiacritics(string stIn)
        {
            string stFormD = stIn.Normalize(NormalizationForm.FormD);
            StringBuilder sb = new StringBuilder();
    
            for (int ich = 0; ich < stFormD.Length; ich++)
            {
                UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(stFormD[ich]);
                if (uc != UnicodeCategory.NonSpacingMark)
                {
                    sb.Append(stFormD[ich]);
                }
            }
    
            return (sb.ToString().Normalize(NormalizationForm.FormC));
        }
    }
    

    这仍然不能解决非拉丁字符问题。一个完全替代的解决方案是Uri.EscapeDataString要将字符串转换为十六进制表示形式:

    string original = "测试公司";
    
    // %E6%B5%8B%E8%AF%95%E5%85%AC%E5%8F%B8
    string converted = Uri.EscapeDataString(original);
    

    然后使用数据生成超链接:

    <a href="http://www.example.com/100/%E6%B5%8B%E8%AF%95%E5%85%AC%E5%8F%B8">
        测试公司
    </a>
    

    许多浏览器会在地址栏中显示中文字符(见下文),但基于我有限的测试,它并不完全受支持。

    address bar with Chinese characters

    注:为了uri.escapedatastring这样工作,iriParsing必须启用。


    编辑

    对于那些希望在C_中生成URL段塞的用户,我建议查看以下相关问题:

    How does Stack Overflow generate its SEO-friendly URLs?

    这是我最终用于我的项目的。

        4
  •  4
  •   Matthew Groves    16 年前

    我有一个问题是鼻塞(新词!)是碰撞。例如,如果我有一篇博文叫做“栈溢出”,还有一篇叫做“栈溢出”,那么这两个标题的slug是相同的。因此,我的slug生成器通常必须以某种方式涉及数据库。这可能就是为什么你看不到更多的通用解决方案的原因。

        5
  •  3
  •   Simon Mourier    11 年前

    这是我的照片。它支持:

    • 删除音调符号(因此我们不只是删除“无效”字符)
    • 结果的最大长度(或删除音调符号之前-“早期截断”)。
    • 规范化块之间的自定义分隔符
    • 结果可以强制为大写或小写
    • 支持的Unicode类别的可配置列表
    • 可配置的允许字符范围列表
    • 支持框架2.0

    代码:

    /// <summary>
    /// Defines a set of utilities for creating slug urls.
    /// </summary>
    public static class Slug
    {
        /// <summary>
        /// Creates a slug from the specified text.
        /// </summary>
        /// <param name="text">The text. If null if specified, null will be returned.</param>
        /// <returns>
        /// A slugged text.
        /// </returns>
        public static string Create(string text)
        {
            return Create(text, (SlugOptions)null);
        }
    
        /// <summary>
        /// Creates a slug from the specified text.
        /// </summary>
        /// <param name="text">The text. If null if specified, null will be returned.</param>
        /// <param name="options">The options. May be null.</param>
        /// <returns>A slugged text.</returns>
        public static string Create(string text, SlugOptions options)
        {
            if (text == null)
                return null;
    
            if (options == null)
            {
                options = new SlugOptions();
            }
    
            string normalised;
            if (options.EarlyTruncate && options.MaximumLength > 0 && text.Length > options.MaximumLength)
            {
                normalised = text.Substring(0, options.MaximumLength).Normalize(NormalizationForm.FormD);
            }
            else
            {
                normalised = text.Normalize(NormalizationForm.FormD);
            }
            int max = options.MaximumLength > 0 ? Math.Min(normalised.Length, options.MaximumLength) : normalised.Length;
            StringBuilder sb = new StringBuilder(max);
            for (int i = 0; i < normalised.Length; i++)
            {
                char c = normalised[i];
                UnicodeCategory uc = char.GetUnicodeCategory(c);
                if (options.AllowedUnicodeCategories.Contains(uc) && options.IsAllowed(c))
                {
                    switch (uc)
                    {
                        case UnicodeCategory.UppercaseLetter:
                            if (options.ToLower)
                            {
                                c = options.Culture != null ? char.ToLower(c, options.Culture) : char.ToLowerInvariant(c);
                            }
                            sb.Append(options.Replace(c));
                            break;
    
                        case UnicodeCategory.LowercaseLetter:
                            if (options.ToUpper)
                            {
                                c = options.Culture != null ? char.ToUpper(c, options.Culture) : char.ToUpperInvariant(c);
                            }
                            sb.Append(options.Replace(c));
                            break;
    
                        default:
                            sb.Append(options.Replace(c));
                            break;
                    }
                }
                else if (uc == UnicodeCategory.NonSpacingMark)
                {
                    // don't add a separator
                }
                else
                {
                    if (options.Separator != null && !EndsWith(sb, options.Separator))
                    {
                        sb.Append(options.Separator);
                    }
                }
    
                if (options.MaximumLength > 0 && sb.Length >= options.MaximumLength)
                    break;
            }
    
            string result = sb.ToString();
    
            if (options.MaximumLength > 0 && result.Length > options.MaximumLength)
            {
                result = result.Substring(0, options.MaximumLength);
            }
    
            if (!options.CanEndWithSeparator && options.Separator != null && result.EndsWith(options.Separator))
            {
                result = result.Substring(0, result.Length - options.Separator.Length);
            }
    
            return result.Normalize(NormalizationForm.FormC);
        }
    
        private static bool EndsWith(StringBuilder sb, string text)
        {
            if (sb.Length < text.Length)
                return false;
    
            for (int i = 0; i < text.Length; i++)
            {
                if (sb[sb.Length - 1 - i] != text[text.Length - 1 - i])
                    return false;
            }
            return true;
        }
    }
    
    /// <summary>
    /// Defines options for the Slug utility class.
    /// </summary>
    public class SlugOptions
    {
        /// <summary>
        /// Defines the default maximum length. Currently equal to 80.
        /// </summary>
        public const int DefaultMaximumLength = 80;
    
        /// <summary>
        /// Defines the default separator. Currently equal to "-".
        /// </summary>
        public const string DefaultSeparator = "-";
    
        private bool _toLower;
        private bool _toUpper;
    
        /// <summary>
        /// Initializes a new instance of the <see cref="SlugOptions"/> class.
        /// </summary>
        public SlugOptions()
        {
            MaximumLength = DefaultMaximumLength;
            Separator = DefaultSeparator;
            AllowedUnicodeCategories = new List<UnicodeCategory>();
            AllowedUnicodeCategories.Add(UnicodeCategory.UppercaseLetter);
            AllowedUnicodeCategories.Add(UnicodeCategory.LowercaseLetter);
            AllowedUnicodeCategories.Add(UnicodeCategory.DecimalDigitNumber);
            AllowedRanges = new List<KeyValuePair<short, short>>();
            AllowedRanges.Add(new KeyValuePair<short, short>((short)'a', (short)'z'));
            AllowedRanges.Add(new KeyValuePair<short, short>((short)'A', (short)'Z'));
            AllowedRanges.Add(new KeyValuePair<short, short>((short)'0', (short)'9'));
        }
    
        /// <summary>
        /// Gets the allowed unicode categories list.
        /// </summary>
        /// <value>
        /// The allowed unicode categories list.
        /// </value>
        public virtual IList<UnicodeCategory> AllowedUnicodeCategories { get; private set; }
    
        /// <summary>
        /// Gets the allowed ranges list.
        /// </summary>
        /// <value>
        /// The allowed ranges list.
        /// </value>
        public virtual IList<KeyValuePair<short, short>> AllowedRanges { get; private set; }
    
        /// <summary>
        /// Gets or sets the maximum length.
        /// </summary>
        /// <value>
        /// The maximum length.
        /// </value>
        public virtual int MaximumLength { get; set; }
    
        /// <summary>
        /// Gets or sets the separator.
        /// </summary>
        /// <value>
        /// The separator.
        /// </value>
        public virtual string Separator { get; set; }
    
        /// <summary>
        /// Gets or sets the culture for case conversion.
        /// </summary>
        /// <value>
        /// The culture.
        /// </value>
        public virtual CultureInfo Culture { get; set; }
    
        /// <summary>
        /// Gets or sets a value indicating whether the string can end with a separator string.
        /// </summary>
        /// <value>
        ///   <c>true</c> if the string can end with a separator string; otherwise, <c>false</c>.
        /// </value>
        public virtual bool CanEndWithSeparator { get; set; }
    
        /// <summary>
        /// Gets or sets a value indicating whether the string is truncated before normalization.
        /// </summary>
        /// <value>
        ///   <c>true</c> if the string is truncated before normalization; otherwise, <c>false</c>.
        /// </value>
        public virtual bool EarlyTruncate { get; set; }
    
        /// <summary>
        /// Gets or sets a value indicating whether to lowercase the resulting string.
        /// </summary>
        /// <value>
        ///   <c>true</c> if the resulting string must be lowercased; otherwise, <c>false</c>.
        /// </value>
        public virtual bool ToLower
        {
            get
            {
                return _toLower;
            }
            set
            {
                _toLower = value;
                if (_toLower)
                {
                    _toUpper = false;
                }
            }
        }
    
        /// <summary>
        /// Gets or sets a value indicating whether to uppercase the resulting string.
        /// </summary>
        /// <value>
        ///   <c>true</c> if the resulting string must be uppercased; otherwise, <c>false</c>.
        /// </value>
        public virtual bool ToUpper
        {
            get
            {
                return _toUpper;
            }
            set
            {
                _toUpper = value;
                if (_toUpper)
                {
                    _toLower = false;
                }
            }
        }
    
        /// <summary>
        /// Determines whether the specified character is allowed.
        /// </summary>
        /// <param name="character">The character.</param>
        /// <returns>true if the character is allowed; false otherwise.</returns>
        public virtual bool IsAllowed(char character)
        {
            foreach (var p in AllowedRanges)
            {
                if (character >= p.Key && character <= p.Value)
                    return true;
            }
            return false;
        }
    
        /// <summary>
        /// Replaces the specified character by a given string.
        /// </summary>
        /// <param name="character">The character to replace.</param>
        /// <returns>a string.</returns>
        public virtual string Replace(char character)
        {
            return character.ToString();
        }
    }
    
    推荐文章