2010-02-03 5 views
5

Ma première question ici à donc!
Au point; Je suis assez nouveau quand il s'agit d'expressions régulières.
Pour apprendre un peu mieux et créer quelque chose que je peux réellement utiliser, j'essaie de créer une expression rationnelle qui trouvera toutes les balises CSS dans un fichier CSS.

Jusqu'à présent, j'utilise:
Essayer de supprimer les codes hexadécimaux des résultats d'expressions régulières

[#.]([a-zA-Z0-9_\-])* 

Ce qui fonctionne très bien et trouve le #TB_window ainsi que la #TB_window img#TB_Image et la .TB_Image#TB_window.

Le problème est qu'il trouve également les étiquettes de code hexadécimal dans le fichier CSS. c'est-à-dire #FFF ou #eaeaea.
Le .png ou .jpg ou et 0.75 sont trouvés aussi ..

En fait, il est assez logique qu'ils soient trouvés, mais n'y at-il pas des solutions de contournement intelligentes pour cela?
Comme excluant quoi que ce soit entre les parenthèses {..}?
(Je suis assez sûr que c'est possible, mais mon expérience regexp n'est pas encore énorme).

Merci d'avance!

À la votre!
Mike

Répondre

2

CSS est un langage très simple et régulier, ce qui signifie qu'il peut être complètement analysé par Regex. Tout ce qu'il y a sont des groupes de sélecteurs, chacun suivi d'un groupe d'options séparées par des deux-points.

Notez que tous les regexes dans ce poste devraient avoir le bavard et dotall drapeaux fixés (/ s et/x dans certaines langues, re.DOTALL et re.VERBOSE en Python).

Pour obtenir des paires de (sélecteurs, règles):

\s*  # Match any initial space 
([^{}]+?) # Ungreedily match a string of characters that are not curly braces. 
\s*  # Arbitrary spacing again. 
\{   # Opening brace. 
    \s*  # Arbitrary spacing again. 
    (.*?) # Ungreedily match anything any number of times. 
    \s*  # Arbitrary spacing again. 
\}   # Closing brace. 

Cela ne fonctionnera pas dans les rares cas d'avoir une accolade cité dans un sélecteur d'attribut (par exemple img[src~='{abc}']) ou dans une règle (par exemple background: url('images/ab{c}.jpg')). Cela peut être résolu en ce qui complique un peu plus la regex:

\s*  # Match any initial space 
((?:  # Start the selectors capture group. 
    [^{}\"\']   # Any character other than braces or quotes. 
    |     # OR 
    \"     # An opening double quote. 
    (?:[^\"\\]|\\.)* # Either a neither-quote-not-backslash, or an escaped character. 
    \"     # And a closing double quote. 
    |     # OR 
    \'(?:[^\']|\\.)*\' # Same as above, but for single quotes. 
)+?)  # Ungreedily match all that once or more. 
\s*  # Arbitrary spacing again. 
\{   # Opening brace. 
    \s*  # Arbitrary spacing again. 
    ((?:[^{}\"\']|\"(?:[^\"\\]|\\.)*\"|\'(?:[^\'\\]|\\.)*\')*?) 
      # The above line is the same as the one in the selector capture group. 
    \s*  # Arbitrary spacing again. 
\}   # Closing brace. 
# This will even correctly identify escaped quotes. 

Woah, c'est une poignée. Mais si vous l'approchez d'une manière modulaire, vous remarquerez que ce n'est pas aussi complexe qu'il n'y paraît à première vue. Maintenant, pour diviser les sélecteurs et les règles, nous devons faire correspondre des chaînes de caractères qui ne sont pas des délimiteurs (où un délimiteur est la virgule pour les sélecteurs et un point-virgule pour les règles) ou des chaînes entre guillemets. Nous allons utiliser le même modèle que nous avons utilisé ci-dessus.

Pour sélecteurs:

\s*  # Match any initial space 
((?:  # Start the selectors capture group. 
    [^,\"\']    # Any character other than commas or quotes. 
    |     # OR 
    \"     # An opening double quote. 
    (?:[^\"\\]|\\.)* # Either a neither-quote-not-backslash, or an escaped character. 
    \"     # And a closing double quote. 
    |     # OR 
    \'(?:[^\'\\]|\\.)*\' # Same as above, but for single quotes. 
)+?)  # Ungreedily match all that. 
\s*  # Arbitrary spacing. 
(?:,|$)  # Followed by a comma or the end of a string. 

Pour connaître les règles:

\s*  # Match any initial space 
((?:  # Start the selectors capture group. 
    [^,\"\']    # Any character other than commas or quotes. 
    |     # OR 
    \"     # An opening double quote. 
    (?:[^\"\\]|\\.)* # Either a neither-quote-not-backslash, or an escaped character. 
    \"     # And a closing double quote. 
    |     # OR 
    \'(?:[^\'\\]|\\.)*\' # Same as above, but for single quotes. 
)+?)  # Ungreedily match all that. 
\s*  # Arbitrary spacing. 
(?:;|$)  # Followed by a semicolon or the end of a string. 

Enfin, pour chaque règle, on peut diviser (une fois!) Sur deux points pour obtenir une paire valeur de la propriété.

Mettre tout cela ensemble dans un programme Python (les expressions rationnelles sont les mêmes que ci-dessus, mais non verbeux pour économiser l'espace):

import re 

CSS_FILENAME = 'C:/Users/Max/frame.css' 

RE_BLOCK = re.compile(r'\s*((?:[^{}"\'\\]|\"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')+?)\s*\{\s*((?:[^{}"\'\\]|"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')*?)\s*\}', re.DOTALL) 
RE_SELECTOR = re.compile(r'\s*((?:[^,"\'\\]|\"(?:[^"\\]|\\.)*\"|\'(?:[^\'\\]|\\.)*\')+?)\s*(?:,|$)', re.DOTALL) 
RE_RULE = re.compile(r'\s*((?:[^;"\'\\]|\"(?:[^"\\]|\\.)*\"|\'(?:[^\'\\]|\\.)*\')+?)\s*(?:;|$)', re.DOTALL) 

css = open(CSS_FILENAME).read() 

print [(RE_SELECTOR.findall(i), 
     [re.split('\s*:\s*', k, 1) 
     for k in RE_RULE.findall(j)]) 
     for i, j in RE_BLOCK.findall(css)] 

Pour cet échantillon CSS:

body, p#abc, #cde, a img .fgh, * { 
    font-size: normal; background-color: white !important; 

    -webkit-box-shadow: none 
} 

#test[src~='{a\'bc}'], .tester { 
    -webkit-transition: opacity 0.35s linear; 
    background: white !important url("abc\"cd'{e}.jpg"); 
    border-radius: 20px; 
    opacity: 0; 
    -webkit-box-shadow: rgba(0, 0, 0, 0.6) 0px 0px 18px; 
} 

span {display: block;} .nothing{} 

. .. nous obtenons (Spaced pour plus de clarté):

[(['body', 
    'p#abc', 
    '#cde', 
    'a img .fgh', 
    '*'], 
    [['font-size', 'normal'], 
    ['background-color', 'white !important'], 
    ['-webkit-box-shadow', 'none']]), 
(["#test[src~='{a\\'bc}']", 
    '.tester'], 
    [['-webkit-transition', 'opacity 0.35s linear'], 
    ['background', 'white !important url("abc\\"cd\'{e}.jpg")'], 
    ['border-radius', '20px'], 
    ['opacity', '0'], 
    ['-webkit-box-shadow', 'rgba(0, 0, 0, 0.6) 0px 0px 18px']]), 
(['span'], 
    [['display', 'block']]), 
(['.nothing'], 
    [])] 

exercice simple pour le lecteur: écrire une expression rationnelle pour enlever CSS com ments (/* ... */).

0

Tout d'abord, je ne vois pas comment le RE vous avez publié trouverait .TB_Image#TB_window. Vous pouvez faire quelque chose comme:

/^[#\.]([a-zA-Z0-9_\-]*)\s*{?\s*$/ 

Ce trouverait toutes les occurrences de # ou . au début de la ligne, suivie par la balise, éventuellement suivie d'un { puis une nouvelle ligne.

Notez que cela ne fonctionnerait PAS pour les lignes comme .TB_Image { something: 0; } (toutes sur une ligne) ou div.mydivclass puisque le . n'est pas au début de la ligne.

Modifier: Je ne pense pas que les accolades imbriquées sont autorisées dans les CSS, donc si vous lisez dans toutes les données et de se débarrasser de nouvelles lignes, vous pourriez faire quelque chose comme:

/([a-zA-Z0-9_\-]*([#\.][a-zA-Z0-9_\-]+)+\s*,?\s*)+{.*}/ 

Il y a une façon dire à une regex d'ignorer les nouvelles lignes, mais je ne semble jamais avoir raison.

+0

Et cela ne fonctionne pas pour '.foo, .bar'. –

1

Qu'en est-ce:

([#.]\S+\s*,?)+(?=\{) 
0

Il est en fait pas une tâche facile à résoudre avec des expressions régulières car il y a beaucoup de possibilités, tenez compte:

  • sélecteurs descendants comme #someid ul img - ce sont toutes les étiquettes valides et sont séparées par des espaces
  • les étiquettes qui ne commencent pas par . ou # (c.-à-d.les noms de balises HTML) - vous devez fournir une liste des personnes afin de les faire correspondre car ils ont pas d'autre différence attributs
  • commentaires
  • plus que je ne peux pas penser à ce moment

Je pense que vous devriez plutôt considérer une bibliothèque d'analyse CSS adaptée à votre langue préférée.

+0

Vous voulez ajouter une raison pour downvote? –

Questions connexes