9

url()CSS ファイルからすべての URL (式) を取得する必要があります。例えば:

b { background: url(img0) }
b { background: url("img1") }
b { background: url('img2') }
b { background: url( img3 ) }
b { background: url( "img4" ) }
b { background: url( 'img5' ) }
b { background: url (img6) }
b { background: url ("img7") }
b { background: url ('img8') }
{ background: url('noimg0) }
{ background: url(noimg1') }
/*b { background: url(noimg2) }*/
b { color: url(noimg3) }
b { content: 'url(noimg4)' }
@media screen and (max-width: 1280px) { b { background: url(img9) } }
b { background: url(img10) }

img*URL ではなく、すべての URL を取得する必要がありnoimg*ます (無効な構文、無効なプロパティ、またはコメント内)。

古き良き正規表現を使ってみました。いくつかの試行錯誤の後、私はこれを得ました:

private static IEnumerable<string> ParseUrlsRegex (string source)
{
    var reUrls = new Regex(@"(?nx)
        url \s* \( \s*
            (
                (?! ['""] )
                (?<Url> [^\)]+ )
                (?<! ['""] )
                |
                (?<Quote> ['""] )
                (?<Url> .+? )
                \k<Quote>
            )
        \s* \)");
    return reUrls.Matches(source)
        .Cast<Match>()
        .Select(match => match.Groups["Url"].Value);
}

これはクレイジーな正規表現の 1 つですが、それでも機能しません。3 つの無効な URL (つまり、2、3、4) に一致します。さらに、誰もが複雑な文法の解析に正規表現を使用するのは間違っていると言うでしょう。

別のアプローチを試してみましょう。この質問によると、実行可能な唯一のオプションはExCSS です(他のオプションは単純すぎるか、時代遅れです)。ExCSSを使用すると、次のようになりました。

    private static IEnumerable<string> ParseUrlsExCss (string source)
    {
        var parser = new StylesheetParser();
        parser.Parse(source);
        return parser.Stylesheet.RuleSets
            .SelectMany(i => i.Declarations)
            .SelectMany(i => i.Expression.Terms)
            .Where(i => i.Type == TermType.Url)
            .Select(i => i.Value);
    }

正規表現ソリューションとは異なり、これは無効な URL をリストしません。しかし、いくつかの有効なものがリストされていません! つまり、9 と 10 です。これは一部の CSS 構文の既知の問題であり、ライブラリ全体を最初から書き直さない限り修正できないようです。ANTLRの書き換えは放棄されたようです。

質問: CSS ファイルからすべての URL を抽出する方法を教えてください。(上記の例として提供されているものだけでなく、CSS ファイルを解析する必要があります。「noimg」をチェックしたり、1 行の宣言を想定したりしないでください。)

NBこれは「ツールの推奨事項」の質問ではありません。コードの一部、上記のソリューションの1つに対する修正、ライブラリなど、どのソリューションでも問題ありません。必要な機能を明確に定義しました。

4

9 に答える 9

6

最後に、Mozilla Firefox からの CSS パーサーの移植であるAlba.CsCssが動作するようになりました。

何よりもまず、質問には2つのエラーが含まれています:

  1. url (img)CSS 文法ではurlとの間でスペースを使用できないため、構文が正しくありません。(したがって、「img6」、「img7」、および「img8」は URL として返されません。

  2. url関数 ( )の閉じていない引用符url('img)は、重大な構文エラーです。Firefox を含む Web ブラウザーは、それから回復しないようで、CSS ファイルの残りを単にスキップします。したがって、パーサーに "img9" と "img10" を返すように要求する必要はありません (ただし、問題のある 2 つの行が削除されている場合は必要です)。

CsCss には 2 つの解決策があります。

最初の解決策は、トークナイザーのみに依存することCssScannerです。

List<string> uris = new CssLoader().GetUris(source).ToList();

これにより、すべての「img」URL が返されますが (上記のエラー #1 に記載されているものを除く)、プロパティ名がチェックされていないため、「noimg3」も含まれます。

2 番目の解決策は、CSS ファイルを適切に解析することです。これは、ブラウザーの動作に最もよく似ています (閉じられていない引用の後の解析の停止を含む)。

var css = new CssLoader().ParseSheet(source, SheetUri, BaseUri);
List<string> uris = css.AllStyleRules
    .SelectMany(styleRule => styleRule.Declaration.AllData)
    .SelectMany(prop => prop.Value.Unit == CssUnit.List
        ? prop.Value.List : new[] { prop.Value })
    .Where(value => value.Unit == CssUnit.Url)
    .Select(value => value.OriginalUri)
    .ToList();

問題のある 2 行を削除すると、正しい「img」URL がすべて返されます。

background-image( CSS3 のプロパティには URL のリストを含めることができるため、LINQ クエリは複雑です。)

于 2013-08-25T14:11:58.567 に答える
2

私の意見では、複雑な RegExp を作成しすぎました。動作するものは次のとおりですurl\s*[(][\s'""]*(?<Url>img[\w]*)[\s'""]*[)]。私が探しているものを説明しようとします:

  1. 皮切りにurl
  2. その後のすべての空白 ( \s*)
  3. 次はちょうど 1 つの左角かっこ ( [(])
  4. 空白、"、' ( [\s'""]*)などの 0 個以上の文字
  5. 次に「URL」なのでimg、0文字以上の英数字( (?<Url>img[\w]*))で始まり、終わるもの
  6. ここでも、空白、"、' ( [\s'""]*)のような 0 個以上の文字
  7. 右ブラケットで終了[)]

完全な作業コード:

        var source =
            "b { background: url(img0) }\n" +
            "b { background: url(\"img1\") }\n" +
            "b { background: url(\'img2\') }\n" +
            "b { background: url( img3 ) }\n" +
            "b { background: url( \"img4\" ) }\n" +
            "b { background: url( \'img5\' ) }\n" +
            "b { background: url (img6) }\n" +
            "b { background: url (\"img7\") }\n" +
            "b { background: url (\'img8\') }\n" +
            "{ background: url(\'noimg0) }\n" +
            "{ background: url(noimg1\') }\n" +
            "/*b { background: url(noimg2) }*/\n" +
            "b { color: url(noimg3) }\n" +
            "b { content: \'url(noimg4)\' }\n" +
            "@media screen and (max-width: 1280px) { b { background: url(img9) } }\n" +
            "b { background: url(img10) }";


        string strRegex = @"url\s*[(][\s'""]*(?<Url>img[\w]*)[\s'""]*[)]";
        var reUrls = new Regex(strRegex);

        var result = reUrls.Matches(source)
                           .Cast<Match>()
                           .Select(match => match.Groups["Url"].Value).ToArray();
        bool isOk = true;
        for (var i = 0; i <= 10; i++)
        {
            if (!result.Contains("img" + i))
            {
                Console.WriteLine("Missing img"+i);
                isOk = false;
            }
        }
        for (var i = 0; i <= 4; i++)
        {
            if (result.Contains("noimg" + i))
            {
                Console.WriteLine("Redundant noimg" + i);
                isOk = false;
            }
        }
        if (isOk)
        {
            Console.WriteLine("Yes. It is ok :). The result is:");
            foreach (var s in result)
            {
                Console.WriteLine(s);
            }

        }
        Console.ReadLine();
于 2013-08-20T07:46:13.670 に答える
1

おそらく最もエレガントな解決策ではありませんが、必要な仕事をしているようです。

public static List<string> GetValidUrlsFromCSS(string cssStr)
{
    //Enter properties that can validly contain a URL here (in lowercase):
    List<string> validProperties = new List<string>(new string[] { "background", "background-image" });

    List<string> validUrls = new List<string>();
    //We'll use your regex for extracting the valid URLs
    var reUrls = new Regex(@"(?nx)
        url \s* \( \s*
            (
                (?! ['""] )
                (?<Url> [^\)]+ )
                (?<! ['""] )
                |
                (?<Quote> ['""] )
                (?<Url> .+? )
                \k<Quote>
            )
        \s* \)");
    //First, remove all the comments
    cssStr = Regex.Replace(cssStr, "\\/\\*.*?\\*\\/", String.Empty);
    //Next remove all the the property groups with no selector
    string oldStr;
    do
    {
        oldStr = cssStr;
        cssStr = Regex.Replace(cssStr, "(^|{|})(\\s*{[^}]*})", "$1");
    } while (cssStr != oldStr);
    //Get properties
    var matches = Regex.Matches(cssStr, "({|;)([^:{;]+:[^;}]+)(;|})");
    foreach (Match match in matches)
    {
        string matchVal = match.Groups[2].Value;
        string[] matchArr = matchVal.Split(':');
        if (validProperties.Contains(matchArr[0].Trim().ToLower()))
        {
            //Since this is a valid property, extract the URL (if there is one)
            MatchCollection validUrlCollection = reUrls.Matches(matchVal);
            if (validUrlCollection.Count > 0)
            {
                validUrls.Add(validUrlCollection[0].Groups["Url"].Value);
            }
        }
    }
    return validUrls;
}
于 2013-08-20T15:28:27.563 に答える
1

このような問題の場合、より単純なアプローチでうまくいく可能性があります。

  1. すべての CSS コマンドを行で分割します (CSS が簡略化されていると仮定します)。この場合、「;」で分割します。または「}」コマンド。

  2. url(*) 内のすべてのオカレンスを、間違ったものも含めて読み取ります。

  3. 実際に適格な行を検出するコマンド パターンを使用してパイプラインを作成する

    • 3.1 Command1 (コメント検出)
    • 3.2 Command2 (構文エラー URL の検出)
    • 3.3 ...
  4. OK 行にフラグを立てて、OK URL を抽出します。

これは単純なアプローチであり、問​​題を効率的に解決し、非常に複雑で扱いにくい魔法の正規表現を使用しません。

于 2013-08-26T13:48:32.663 に答える
1

/*次の*/ようなものがないかどうかを確認するには、否定的な後読みが必要です。

(?<!\/\*([^*]|\*[^\/])*)

これは判読できないようです。つまり、次のことを意味します。

(?<!-> この試合の前に次のことはできません:

\/\*-> /* (エスケープスラッシュ付き) の後に続く

([^*]-> そうでない任意の文字*

|\*[^\/])-> または文字である *、それ自体はそうでないものが続く/

*)-> このnot a * or a * without a /文字の 0 個以上を持つことができ、最後に負の後読みを閉じます

また、設定されているプロパティが値を受け入れる css プロパティであるかどうかを確認するには、肯定的な後読みが必要url()です。たとえば、だけに興味があるbackground:場合background-image:、これは正規表現全体になります。

(?<!\/\*([^*]|\*[^\/])*)
(?<=background(?:-image)?:\s*)
url\s*\(\s*(('|")?)[^\n'"]+\1\s*\)

このバージョンではurl() の前にcss プロパティbackground:またはが必要なため、 . 単純なパイプを使用して、より受け入れられる CSS プロパティを追加できます。background-image:'url(noimg4)'(?<=(?:border-image|background(?:-image)?):\s*)

\1その構文に慣れていないためではなく、使用しまし\k<Quote>た。つまり、不要なサブグループをキャプチャしないようにするために ?: が必要です。私がテストできる限り、これは機能します。

最後に、コメントから url('img(1)') が機能するはずであり、OPからはそれを解析しない[^\n'"]ことを理解しているため、実際のURLに使用しました。[^\)]

于 2013-08-24T18:38:00.997 に答える