Stack OverflowはどのようにしてSEO対応のURLを生成しますか?


253

タイトルを取得するのに適した完全な正規表現またはその他のプロセスとは何ですか。

Stack OverflowのようにタイトルをURLの一部に変更するにはどうすればよいですか?

そしてそれを

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

それはスタックオーバーフローのSEO対応のURLで使用されますか?

私が使用している開発環境はRuby on Railsですが、他のプラットフォーム固有のソリューション(.NET、PHP、Django)がある場合は、それらも参照してください。

私(または別の読者)は、将来、別のプラットフォームで同じ問題に遭遇すると確信しています。

私はカスタムルートを使用しています。主に、文字列をすべての特殊文字が削除され、すべて小文字になり、すべての空白が置き換えられるように文字列を変更する方法を知りたいです。


面白いキャラクターはどうですか?それらについて何をしますか?ウムラウト?句読点?これらを考慮する必要があります。基本的に、上記のブラックリストアプローチとは対照的に、ホワイトリストアプローチを使用します。許可する文字、変換する文字(何に変換するか)を説明し、残りを意味のあるもの( "")に変更します。 。これを1つの正規表現で実行できるとは思えません...キャラクターをループするだけではどうですか?
ダレン・トーマス

1
メタに移行する必要があります。質問と回答はどちらも特にSOの実装を扱っており、受け入れられた回答は@JeffAtwoodからのものです。
casperOne 2011年

19
@casperOneジェフはメタ以外の評判が許可されていないと思いますか?問題は、「ここでこれをどのように行うことができるか」ではなく、「どのようにこのようなことを行うことができるか」についてです。
パウロEbermann

@PaŭloEbermann:それはジェフがメタ以外の評判を得ることについてではありません(彼が持っている評判はどれほど本当に私の関心事ではありません); 質問の本文では、StackOverflowの実装を具体的に参照していたため、それがメタにある根拠を示しています。
casperOne

回答:


300

以下がその方法です。一見して気付くよりも多くのエッジ条件があることに注意してください。

これは2番目のバージョンで、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 2009年

22
そのスタブ@oskar RemapInternationalCharToAscii()機能がありmeta.stackexchange.com/questions/7435/...
ジェフ・アトウッド

3
これは素晴らしい。これまでに行った唯一の変更は、「if(i == maxlen)break;」を変更することです。「if(sb.Length == maxlen)break;」になる 渡した文字列に無効な文字がたくさんある場合に備えて
Tom Chantler、

4
小さな最適化:if (prevdash) sb.Length -= 1; return sb.ToString();最後のifステートメントの代わり。
Mark Hurd

8
@Dommer sb.Length == maxlen break;はバグがあり、maxLenght-1のサインが "ß"の場合、 "ss"に変換されsb.Length == maxleneますがtrueになることはないため、代わりにをテストすることをお勧めし(sb.Length > = maxlen)ます。
HenrikStenbæk2012年

32

これがジェフのコードの私のバージョンです。次の変更を加えました。

  • ハイフンは追加できるように追加されたため、文字列の最後の文字だったため、削除する必要があります。つまり、「my-slug-」が必要になることはありません。これは、この特別なケースでそれを削除するための追加の文字列割り当てを意味します。これを遅延ハイフンで回避しました。私のコードをJeffのコードと比較すると、このロジックを理解するのは簡単です。
  • 彼のアプローチは純粋にルックアップベースであり、スタックオーバーフローの調査中に例で見つけた多くの文字を見落としました。これに対抗するために、私は最初に正規化パス(メタスタックオーバーフローの質問完全な(プロファイル)URLから削除されたUS-ASCII以外の文字で言及された照合)を実行し、次に許容範囲外の文字を無視します。これはほとんどの場合機能します...
  • ...そうでない場合は、ルックアップテーブルも追加する必要がありました。上記のように、一部の文字は、正規化されたときに低いASCII値にマップされません。これらをドロップするのではなく、穴の完全な例外の手動リストを取得していますが、何もないよりはましです。正規化コードは、スタックオーバーフローの質問でのJon Hannaの素晴らしい投稿に触発されました文字列のアクセントを削除するにはどうすればよいですか?
  • 大文字小文字の変換もオプションになりました。

    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スキームがスタックオーバーフローよりも少し賢い理由の説明については、ブログでこれの拡張バージョンを取得しています。


4
+1これは素晴らしいダンです。私はあなたのブログにもコメントを追加しましたif (i == maxlen) break;が、if (sb.Length == maxlen) break;代わりに変更される可能性があるため、空白文字や無効な文字を多く含む文字列を渡した場合でも、必要な長さのスラッグを取得できます。大幅に切り捨てます(たとえば、80個のスペースで開始する場合を考えてください...)。また、Jeffのコードに対する1,000万回の反復の大まかなベンチマークは、ほぼ同じ速度であることが示されています。
トム・シャントラー

1
おかげで、私のブログに返信し、彼とそこのコードを修正しました。コードのベンチマークにも感謝します。興味のある人にとっては、それはジェフのものと同等でした。
DanH

2
Slug.Create()にいくつかの問題があるようです:ÆØÅの大文字バージョンは正しく変換されませんØはaに変換されている間無視されます。通常、「å」を「aa」に、「ø」を「oe」に、「æ」を「ae」に変換します。2番目(sb.Length == maxlen)の区切り。maxLenght-1のサインが "ß"である場合(sb.Length == maxlen)は決して真実ではないため、代わりに(sb.Length> = maxlen)をテストすることをお勧めします。ランダムな位置でカットし、最後の「-」でカットしないことは抑制されています。これにより、最後に不要な単語で終わるのを防ぐことができます。まるで、最後の「s "
HenrikStenbæk2012年

@DanH javascriptバージョンのコードがあればすばらしいでしょう。
Freshblood、2014

16

カスタムルートを設定して、URLを処理するコントローラーを指すようにします。Ruby on Railsを使用しているため、ここではルーティングエンジンの使用方法を紹介します。

Rubyでは、あなたがすでに知っているような正規表現が必要になります。使用する正規表現は次のとおりです。

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

11

このJavaScript関数は、スラッグのインフォーム生成にも使用できます(これはDjangoに基づいているか、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
}

これはバニラJSではないので、いくつかのレッツやコンストを追加すると素晴らしいでしょう。
Aditya Anand

8

いい目安として、WordPressのPHP関数を次に示します。WordPressは、ファンシーリンクを使用する最も人気のあるプラットフォームの1つだと思います。

    function 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);
            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); //エンティティを殺します
            $ 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

@The How-To Geekの回答を完成させgit clone git://core.git.wordpress.org/wp-includes/formatting.phpファイルを見つけてください
mickro

5

あなたがRailsのエッジを使用している場合は、あなたが頼ることができる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でアクセント(エフェメール)などのエキゾチックな文字を処理する必要がある場合は、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

dbo.UrlEncodeから改変されたT-SQL実装:

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のURLサポートするようになったので、文字以外のすべてを(すべての言語で '-'に)変換する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

Rubyでのブライアンのコード:

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

downcase、小文字に文字列をオンstrip先頭と末尾の空白を、最初の削除gsubコールグラム lobally サブダッシュstitutesスペースを、そして文字またはダッシュではない第二削除しのすべて。


2

これを行う、PermalinkFuと呼ばれる小さなRuby on Railsプラグインがあります。脱出方法が適している文字列への変換を行いURL。コードを見てください。その方法は非常に簡単です。

ASCII文字を削除するには、iconv libを使用して、「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

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

私のテスト文字列:

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


2

stackoverflowのソリューションは素晴らしいですが、現代のブラウザは(いつものように、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をサポートする最初のアプリケーションの1つでした。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

TypeScriptにコードを移植しました。JavaScriptに簡単に適合させることができます。

プロトタイプに.containsメソッドを追加していStringます。最新のブラウザーまたは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

ダメダメダメ。あなたは皆とても間違っている。diacritics-fuのものを除いて、あなたはそこに着いていますが、アジアの文字についてはどうですか(Ruby開発者が日本神の兄弟を考慮しないことを恥とする)。

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

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

「-and-」は、&が「-and-」に変更されるためです。


4
この情報について何を言えばいいのか本当にわかりません。
sjas

3
これは、switch caseステートメントを使用しない場合の非常に良い例です。
NickG 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.