Mewarnai teks dalam html dan Bereaksi

Sangat mudah untuk menambahkan markup ke teks dengan tangan Anda. Anda dapat menandai teks di sini di Habré, dan kemudian menyalinnya ke situs. Anda dapat melakukan pencarian dengan pengganti di Notepad ++ atau di Atom.

Jika 1 teks. Jika ada banyak teks, saya ingin memiliki alat untuk mengekstraksi fragmen teks dengan tag html atau menghasilkan kode sumber untuk Bereaksi. Pada Python, ini tidak sulit (beberapa baris kode per warna).



Jika Anda tahu Python dan ekspresi reguler, ikuti tautannya.

Ada contoh dan kode sumber. Di bawah katom, deskripsi terperinci.

Markup teks misalnya pewarnaan kode sumber Javascript


Pertimbangkan fungsinya:

def jsToHtml(s):

Pada input, teks sumber mengembalikan html.

Kami menetapkan variabel yang menentukan atribut blok. Dalam contoh, gaya digunakan untuk kejelasan, di masa depan lebih logis untuk menggantinya dengan kelas.

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

Markup.

Hal pertama yang harus dilakukan adalah keluar dari karakter '&', '<', '>'

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

'&' lolos untuk menampilkan kombinasi huruf '& lt;', '& gt;' dan lainnya '& ...;',

Karakter '<' dan '>' diloloskan agar tidak bertentangan dengan tag.

Anda dapat melindungi banyak hal, tetapi, menurut saya, dalam utf-8 ini sudah cukup.

Algoritma Markup:

  • Kami mengambil template ulang dan mencari semua fragmen teks yang memuaskannya.
  • Kami memotong setiap fragmen, menambahkan markup ke dalamnya dan menyimpan fragmen yang ditandai dalam sebuah array (pada saat yang sama, menyimpan teks asli: itu akan berguna).
  • Sebagai gantinya, masukkan rintisan dengan nomor.
  • Dan untuk setiap warna.
  • Ketika semuanya dicat, ganti potongan dengan potongan-potongan berwarna dari array.

Rintisan harus unik, tetapi dalam teks kita tidak ada satu karakter '<' dan tidak ada satu '>'.

Membuat rintisan:

f'<{i}>'

di mana saya adalah nomor rintisan. Jelas tidak ada hal seperti itu dalam teks.

Apa yang harus dilakukan jika sudah ada potongan di dalam fragmen.

Misalnya, dalam teks sumber ada baris:

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

Opsi 2:

  1. Mengabaikan. Dalam hal ini, string akan memiliki warna ganda.
  2. Temukan bertopik bersarang dan ganti dengan teks asli (tidak bertanda).

Kami membuat fungsi yang mengimplementasikan semua ini (10 baris kode, 10 baris komentar):

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

Ini adalah demo: markup ekspresi reguler mungkin salah, karakter yang lolos (\ ', \ ") tidak diproses.

Untuk referensi: ekspresi:

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

Notepad ++ dan Atom editor memiliki warna berbeda.

Sekarang ada satu RE , pewarnaan fragmen dilakukan dengan sederhana. Contoh mewarnai garis dalam apostrof dan tanda kutip:

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

Contoh pewarnaan yang lebih kompleks. Perlu js untuk mewarnai ekspresi dalam multi-baris
`    ${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

pertama-tama kita menemukan multi-

string : re.findall (r`` [\ s \ S] +? '', s) mengembalikan daftar blok teks antara karakter backquote.

Setiap blok berisi spasi atau non spasi ([\ s \ S] yaitu apa saja).

Panjang blok 1 atau lebih (+).

Tanpa keserakahan ("?" Berarti tidak ada simbol "` "di dalam blok).

Salin blok yang ditemukan (variabel mStr ) ke variabel newFstr .
Kami menemukan di blokir blokir dengan ekspresi $ {...}.

re.findall (r "\ $ \ {[\ s \ S] *? \}", mStr) mengembalikan daftar sub-blok tersebut. Kami menyimpan markup di ls array dan mengganti sub-blok dengan stub di variabel newFstr .
Ketika sub-blok habis, ganti dengan string asli snilai blok asli ke baru.

Set tidak berlebihan. Jika findall mengembalikan beberapa blok identik, saat memproses blok pertama dalam kode sumber, semua blok identik diganti dengan bertopik sekaligus. Saat memproses blok kedua yang sama, itu tidak lagi berada di teks sumber. Setel menghapus duplikasi.

File JsToHtml.py
# -*- 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 dalam Bereaksi


Ubah html menjadi Bereaksi kode sumber di htmltoreact.com . Ada juga tautan ke GitHub.

Ini tidak cocok untuk saya: pertama, itu tidak membentuk apa yang saya butuhkan, dan kedua, bagaimana saya akan menyeret keajaiban ini ke server saya.

Saya menulis sendiri.

Instal pustaka lxml (pip install lxml atau pip3 install lxml).

Kami mengimpor:

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

Konversi teks html ke teks xhtml. Itu hampir sama, tetapi semua tag ditutup.

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

Parsim xhtml yang dihasilkan ke rumah pohon menggunakan mini-house.

dom = parseString(ht)

Kami membuat fungsi yang secara rekursif melompati node dan menghasilkan hasilnya dalam bentuk kode sumber Bereaksi.

Setelah memanggil parseString, pohon dom adalah simpul ayah yang memiliki simpul anak yang memiliki anak, dll.

Setiap node adalah kamus yang berisi deskripsinya:

  • nodeName - nama simpul, string
  • childNodes - child node, daftar
  • atribut- atribut, kamus
  • Sebuah simpul bernama #text memiliki nodeValue (string)

Contoh:

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

Setelah transformasi kita dapatkan:
{ 'nodeName':'div',
  'attributes': {'style': 'color: red;', 'class': 'A'},
  'childNodes': [
    {'nodeName':'#text', 'nodeValue': 'Red of course,'},
    {'nodeName':'br'},
    {'nodeName':'#text', 'nodeValue': ''},
  ],
}

Mengubah dom ke string mudah (ada jejak), ketika menghasilkan kode Bereaksi, saya mengganti kelas dengan className dan redid atribut style.

Dalam simpul teks, '{', '}', '<', '>' lolos.

File HtmlToReact.py
# -*- 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()}>'

# *** *** ***



Contoh dan kode sumber di sini.

PS On Habré pewarnaan kode Python (dan mungkin yang lain) tidak ideal. Jika string python memiliki string & amp; , ini ditampilkan sebagai & . Dalam kode saya, saya mengubah agar terlihat benar. Jika Habr memperbaiki kesalahan, teks saya akan saling bersilangan.

All Articles