Färben von Texten in HTML und Reagieren

Es ist einfach, mit den Händen Markups zum Text hinzuzufügen. Sie können den Text hier auf Habré markieren und dann auf die Site kopieren. Sie können eine Suche mit einem Ersatz in Notepad ++ oder in Atom durchführen.

Wenn es 1 Text ist. Wenn es viele Texte gibt, möchte ich ein Tool zum Extrahieren von Textfragmenten mit HTML-Tags oder zum Generieren von Quellcode für React haben. Unter Python ist dies nicht schwierig (einige Codezeilen pro Farbe).



Wenn Sie Python und reguläre Ausdrücke kennen, folgen Sie dem Link.

Es gibt Beispiele und Quellcodes. Unter einer Katze eine detaillierte Beschreibung.

Text-Markup zum Beispiel zum Färben von Javascript-Quellcode


Betrachten Sie die Funktion:

def jsToHtml(s):

Bei der Eingabe gibt der Quelltext HTML zurück.

Wir setzen die Variablen, die die Attribute der Blöcke definieren. Im Beispiel werden Stile aus Gründen der Übersichtlichkeit verwendet. In Zukunft ist es logischer, sie durch Klassen zu ersetzen.

comm = 'style="color: red;"' #   
blue = 'style="color: blue;"' # 
...

Markup.

Das erste, was Sie tun müssen, ist, den Zeichen '&', '<', '>' zu entkommen.

s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')

'&' wird maskiert, um die Buchstabenkombinationen '& lt;', '& gt;' anzuzeigen. und andere '& ...;', Die

Zeichen '<' und '>' werden maskiert, um nicht mit Tags in Konflikt zu geraten.

Sie können viele Dinge abschirmen, aber meiner Meinung nach reicht dies in utf-8 aus.

Markup-Algorithmus:

  • Wir nehmen eine neue Vorlage und suchen nach allen Textfragmenten, die diese erfüllen.
  • Wir schneiden jedes Fragment aus, fügen Markups hinzu und speichern das markierte Fragment in einem Array (gleichzeitig speichern wir den Originaltext: es wird nützlich sein).
  • Setzen Sie an seiner Stelle den Stummel mit der Nummer ein.
  • Und so für jede Farbe.
  • Wenn alles gemalt ist, ersetzen Sie die Stummel durch farbige Fragmente aus dem Array.

Der Stub sollte eindeutig sein, aber in unserem Text gibt es kein einziges Zeichen '<' und kein einziges '>'.

Einen Stummel machen:

f'<{i}>'

wo ich die Stichnummer ist. Es gibt definitiv keine solche im Text.

Was tun, wenn sich bereits ein Stummel im Fragment befindet?

Zum Beispiel gab es im Quelltext Zeilen:

`  :   /*     */ `
/* , `     ` */

Optionen 2:

  1. Ignorieren. In diesem Fall hat die Zeichenfolge eine doppelte Farbe.
  2. Suchen Sie verschachtelte Stubs und ersetzen Sie sie durch den ursprünglichen (nicht markierten) Text.

Wir machen eine Funktion, die all dies implementiert (10 Codezeilen, 10 Kommentarzeilen):

def oneRe(reStr, s, attr, ls, multiColor=False):
    '''
        s  ,          ls,
           (<0>, <1> ...<1528>...).
          .
    
    reStr - re
    s - 
    attr -  style/class/etc
    ls -   
    multiColor=False,     
    '''
    for block in set(re.findall(reStr, s)):
        toArr = block
        if not multiColor: #  multiColor==False,   
            for prev in set(re.findall(r'<[\d]+>', block)): #  : <0> ... <21>
                iPrev = int(prev[1:-1], 10)                #     
                toArr = toArr.replace(prev, ls[iPrev][1])  #   '<0> qwe <21>'  (<0>,<21>)  . 
        ls.append([f'<span {attr}>{toArr}</span>', toArr]) #     ls 2 :     
        s = s.replace(block, f'<{len(ls)-1}>')
    return s

Dies ist eine Demo: Das Markup für reguläre Ausdrücke ist möglicherweise falsch, maskierte Zeichen (\ ', \ ") werden nicht verarbeitet.

Als Referenz: Ausdruck:

s = s.replace(/A + B/g, 'A - B');

Notepad ++ - und Atom-Editoren haben unterschiedliche Farben.

Jetzt, wo es oneRe gibt , wird das Färben von Fragmenten einfach durchgeführt. Ein Beispiel zum Färben der Linien in Apostrophen und Anführungszeichen:

s = oneRe(r"'[\s\S]*?'", s, green, ls, multiColor=False) #    ''   ls
s = oneRe(r'"[\s\S]*?"', s, green, ls, multiColor=False) #    ""   ls

Ein Beispiel für eine komplexere Färbung. Benötigen Sie js, um Ausdrücke in mehreren Zeilen zu färben
`    ${A + B}  ${A - B}`


for mStr in set(re.findall(r'`[\s\S]+?`', s)): #    
    newFstr = mStr
    for val in set(re.findall(r"\$\{[\s\S]+?\}", mStr)):
        ls.append([f'<span {darkRed}>{val}</span>', val])
        newFstr = newFstr.replace(val, f'<{i}>')
        i += 1
    s = s.replace(mStr, newFstr)
    
s = oneRe(r'`[\s\S]+?`', s, green, ls, multiColor=True) #    ``   ls

Zuerst finden wir die

Mehrfachzeichenfolgen : re.findall (r`` [\ s \ S] +? '', s) gibt eine Liste von Textblöcken zwischen Backquote-Zeichen zurück.

Jeder Block enthält entweder Leerzeichen oder Nicht-Leerzeichen ([\ s \ S], dh alles).

Blocklänge 1 oder mehr (+).

Ohne Gier ("?" Bedeutet, dass sich im Block kein Symbol "` "befindet).

Kopieren Sie den gefundenen Block ( mStr- Variable ) in die newFstr- Variable .
Wir finden im Block Unterblöcke mit den Ausdrücken $ {...}.

re.findall (r "\ $ \ {[\ s \ S] *? \}", mStr) gibt eine Liste solcher Unterblöcke zurück. Wir speichern das Markup im ls-Array und ersetzen den Unterblock durch den Stub in der Variablen newFstr .
Wenn die Unterblöcke leer sind, ersetzen Sie die ursprüngliche Zeichenfolge sursprünglicher Blockwert auf neu.

Set ist nicht überflüssig. Wenn findall mehrere identische Blöcke zurückgibt, werden bei der Verarbeitung des ersten Blocks im Quellcode alle identischen Blöcke gleichzeitig durch Stubs ersetzt. Wenn der zweite Block verarbeitet wird, befindet er sich nicht mehr im Quelltext. Set entfernt Duplikate.

JsToHtml.py-Datei
# -*- coding: utf-8 -*- 
'''
AON 2020

'''

import re

# *** *** ***

def oneRe(reStr, s, attr, ls, multiColor=False):
    '''
        s  ,          ls,
           (<0>, <1> ...<1528>...).
          .
    
    reStr - re
    s - 
    attr -  style/class/etc
    ls -   
    multiColor=False,     
    '''
    i = len(ls) #   
    for block in set(re.findall(reStr, s)):
        toArr = block
        if not multiColor: #  multiColor==False,   
            for prev in set(re.findall(r'<[\d]+>', block)): #  : <0> ... <21>
                iPrev = int(prev[1:-1], 10)                 #     
                toArr = toArr.replace(prev, ls[iPrev][1])   #   '<0> qwe <21>'  (<0>,<21>)  . 
        ls.append([f'<span {attr}>{toArr}</span>', toArr])  #     ls 2 :     
        s = s.replace(block, f'<{i}>')              #       
        i += 1
    return s

# *** *** ***

def operColor(s, ls, color):
    '''
     .
      ,       ,  
    '''
    i = len(ls)
    for c in ['&lt;=', '&gt;=', '=&lt;', '=&gt;', '&lt;', '&gt;', '&amp;&amp;', '&amp;',
              '===', '!==', '==', '!=', '+=', '-=', '++', '--', '||']:
        ls.append([f'<span {color}>{c}</span>',0])
        s = s.replace(c, f'<{i}>')
        i += 1
    for c in '!|=+-?:,.[](){}%*/':
        ls.append([f'<span {color}>{c}</span>',0])
        s = s.replace(c, f'<{i}>')
        i += 1
    return s

# *** *** ***

def jsToHtml(s):
    '''
      .
     ,        <span>.
    '''

    black = '''style="font-family: 'Courier New', monospace;
        background: #fff; 
        color: black;
        font-weight: bold;
        border: 1px solid #ddd;
        padding: 5px;
        text-align: left;
        white-space: pre;"'''
    comm = 'style="color: red;"'
    green = 'style="color: green; font-style: italic;"'
    blue = 'style="color: blue;"'
    red2 = 'style="color: #840;"'

    s = s.replace('&', '&amp;').replace('<', &'&lt;').replace('>', '&gt;')   #   '&', '<', '>'

    ls = []

    i = 0
    for mStr in set(re.findall(r'`[\s\S]+?`', s)): #    
        newFstr = mStr
        for val in set(re.findall(r"\$\{[\s\S]+?\}", mStr)):
            ls.append([f'<span {darkRed}>{val}</span>', val])
            newFstr =newFstr.replace(val, f'<{i}>')
            i += 1
        s = s.replace(mStr, newFstr)
        
    s = oneRe(r'`[\s\S]+?`', s, green, ls, multiColor=True) #    ``   ls
    s = oneRe(r"'[\s\S]*?'", s, green, ls, multiColor=False) #    ''   ls
    s = oneRe(r'"[\s\S]*?"', s, green, ls, multiColor=False) #    ""   ls
    s = oneRe(r'/[\s\S].*?/g\b', s, green, ls, multiColor=False) #    re-   ls
    s = oneRe(r'/[\s\S].*?/\.', s, green, ls, multiColor=False) #    re-   ls
    s = oneRe(r'/\*[\s\S]+?\*/', s, comm, ls, multiColor=False) #    /*  */   ls (  - )
    s = oneRe(r'//[\s\S]*?\n', s, comm, ls, multiColor=False) #    //    ls (  - )

    i = len(ls)

    #    
    for c in ['new', 'JSON', 'Promise', 'then', 'catch', 'let', 'const', 'var', 'true', 'false', 'class', 'from', 'import', 'set', 'list', 'for', 'in', 'if', 'else', 'return', 'null']:
        ls.append([f'<span {blue}>{c}</span>',0])
        s = re.sub (r'\b%s\b' % c, f'<{i}>', s)
        i += 1

    #     
    for c in ['window', 'doc', 'cmd', 'init','init2', 'recalc', 'hide', 'readOnly', 'validate']:
        ls.append([f'<span {darkRed}>{c}</span>',0])
        s = re.sub (r'\b%s\b' % c, f'<{i}>', s)
        i += 1

    s = operColor(s, ls, darkBlue) #    

    for j in range(len(ls), 0, -1):  #  , , ,    
        s = s.replace(f'<{j-1}>', ls[j-1][0])

    return f'<div {black}>{s}</div>'

# *** *** ***


HTML in Reagieren


Konvertieren Sie HTML in React-Quellcode unter htmltoreact.com . Es gibt auch einen Link zu GitHub.

Das passte nicht zu mir: Erstens bildet es nicht genau das, was ich brauche, und zweitens, wie ich dieses Wunder auf meinen Server ziehen werde.

Ich habe meine eigenen geschrieben.

Installieren Sie die lxml-Bibliothek (pip install lxml oder pip3 install lxml).

Wir importieren:

from xml.dom.minidom import parseString
from lxml import html, etree

Konvertieren Sie HTML-Text in XML-Text. Es ist fast dasselbe, aber alle Tags sind geschlossen.

doc = html.fromstring(htmlText)
ht = etree.tostring(doc, encoding='utf-8').decode()

Das resultierende xhtml-Parsim zum Baumhaus mit dem Mini-Haus.

dom = parseString(ht)

Wir machen eine Funktion, die rekursiv über die Knoten springt und das Ergebnis in Form des React-Quellcodes generiert.

Nach dem Aufruf von parseString ist der Dom-Baum ein Papa-Knoten mit untergeordneten Knoten, die untergeordnete Knoten usw. haben.

Jeder Knoten ist ein Wörterbuch mit seiner Beschreibung:

  • Knotenname - Knotenname, Zeichenfolge
  • childNodes - untergeordnete Knoten, Liste
  • Attribute - Attribute, Wörterbuch
  • Ein Knoten namens #text hat nodeValue (string)

Beispiel:

<div class="A" style="color: red;">Red of course,<br> </div>

Nach den Transformationen erhalten wir:
{ 'nodeName':'div',
  'attributes': {'style': 'color: red;', 'class': 'A'},
  'childNodes': [
    {'nodeName':'#text', 'nodeValue': 'Red of course,'},
    {'nodeName':'br'},
    {'nodeName':'#text', 'nodeValue': ''},
  ],
}

Das Konvertieren von dom in eine Zeichenfolge ist einfach (es gibt einen Druck). Beim Generieren des React-Codes habe ich die Klasse durch className ersetzt und das style-Attribut überarbeitet.

In Textknoten werden '{', '}', '<', '>' maskiert.

HtmlToReact.py-Datei
# -*- coding: utf-8 -*- 
# -*- coding: utf-8 -*- 

from xml.dom.minidom import parseString
from lxml import html, etree

# *** *** ***

_react = ''

def htmlToReact(buf):
    '''
    buf - html-
     ReactJS- 
    '''
    global _react
    _react = ''

    try:
        r = re.search('<[\\s\\S]+>', buf)
        if r:
            doc = html.fromstring(r.group(0))
            ht = etree.tostring(doc, encoding='utf-8').decode()
            xHtmlToReact(parseString(ht).childNodes[0], '')
            return _react
        else:
            return '<empty/>'
    except Exception as ex:
        s = f'htmlToReact: \n{ex}'
        print(s)
        return s

# *** *** ***

def sU(a, c):
    '''
    xlink:show   ->  xlinkShow
    font-weight  ->  fontWeight
    '''
    l, _, r = a.partition(c)
    return ( (l + r[0].upper() + r[1:]) if r else a).strip()

# *** *** ***

def xHtmlToReact(n, shift='\n    '):
    '''
       -
      ,        upperCase
       global  _react
    '''
    global _react

    if n.nodeName.lower() in ['head', 'script']:
        return
    
    _react += shift + '<' + n.nodeName.lower()
    if n.attributes:
        for k, v in n.attributes.items():
            if k == 'style':
                style = ''
                for s in v.split(';'):
                    if s.strip():
                        l, _, r = s.partition(':')
                        style += f'''{sU(l, '-')}: "{r.strip()}", '''
                if style:
                    _react += ' style={{' + style + '}}'
            elif k == 'class':
                _react += f' className="{v}"'
            else:
                kk = k.replace('xlink:href', 'href') # deprcated
                _react += f''' {sU( sU(kk, ':'), '-' )}="{v}"'''
        
    _react += '>'
    if n.childNodes:
        for child in n.childNodes:
            if  child.nodeName == '#text':
                tx = child.nodeValue
                for x in ['{', '}', '<', '>']:
                    tx = tx.replace(x, '{"' + x + '"_{_')
                tx = tx.replace('_{_', '}')
                if tx[-1] == ' ':
                    tx = tx[:-1] + '\xa0'
                _react += tx.replace('\n', '<br/>')
            else:
                xHtmlToReact(child)
                
    _react += f'{shift}</{n.nodeName.lower()}>'

# *** *** ***



Beispiele und Quellcode hier.

PS Auf Habré ist die Farbgebung des Python-Codes (und vielleicht anderer) nicht ideal. Wenn die Python-Zeichenfolge die Zeichenfolge & amp; wird als & angezeigt . In meinen Codes habe ich optimiert, damit es richtig aussieht. Wenn Habr einen Fehler korrigiert, kreuzen sich meine Texte.

All Articles