Konvertieren Sie eine ASCII-Art-Tabelle in eine UTF-8-Tabelle

13

Wenn ich Dokumentation, Kommentare usw. schreibe, liebe ich es, ASCII-Tabellen zu erstellen. Normalerweise sehen sie ziemlich gut aus, aber ich bin immer der Meinung, dass sie noch besser aussehen könnten - zumal UTF-8 / Unicode die Box-Zeichen enthält . Die Verwendung dieser Zeichen ist jedoch sehr mühsam, da zum Einfügen mehrere Tastendrücke erforderlich sind. Deine Aufgabe? Schreiben Sie ein Programm oder eine Funktion, die ASCII-Tabellen automatisch in das UTF-8 / Unicode-Äquivalent konvertieren kann.

Diese Herausforderung war Sandkasten .

Herausforderung

Schreiben Sie ein Programm, das eine ASCII-Tabelle als Eingabezeichenfolge angibt und die neu gezeichnete Tabelle mit den Unicode / UTF-8-Box-Zeichen ausgibt. Insbesondere sollten die Zeichen, die Teil der Tabelle sind, wie folgt übersetzt werden:

(Unicode, 3 bytes each in UTF-8)
- to ─ (\u2500)
| to │ (\u2502)
= to ═ (\u2550)

and + to one of:
   ┌ (\u250C), ┐ (\u2510), └ (\u2514), ┘ (\u2518),
   ├ (\u251C), ┤ (\u2524), ┬ (\u252C), ┴ (\u2534),
   ┼ (\u253C)
or, if '=' on either side:
   ╒ (\u2552), ╕ (\u2555), ╘ (\u2558), ╛ (\u255D),
   ╞ (\u255E), ╡ (\u2561), ╤ (\u2564), ╧ (\u2567),
   ╪ (\u256A)

Einzelheiten

I / O:

  • Standard-E / A ist zulässig
  • Sie können Eingaben in jedem vernünftigen Format vornehmen, einschließlich der Tabelle als Zeichenfolge oder als Pfad zu einer Datei, die die Tabelle enthält.
  • Sie können in eine Datei ausgeben und den Dateinamen als zusätzliches Argument verwenden.
    • Allerdings können Sie die Eingabedatei nicht ändern . (Es sollte zur Erleichterung der zukünftigen Bearbeitung beibehalten werden.)

Eingang:

  • Sie können davon ausgehen, dass jede Eingabezeile mit derselben Länge aufgefüllt wurde .
  • Sie können nicht davon ausgehen, dass das erste Zeichen nach einer neuen Zeile ein Teil der Tabellenränder ist (wie es Leerzeichen sein können).
  • Die Eingabe wird als gültige Tabelle betrachtet, wenn alle Zeichen (die Teil der Tabelle sind) -=|mit genau zwei Zeichen +verbunden sind und mit mindestens einem Zeichen sowohl horizontal als auch vertikal verbunden sind.
  • Ihr Programm erzeugt möglicherweise keine Fehler mit gültigen Eingaben.
  • Wenn die Eingabe ungültig ist, ist das Verhalten undefiniert und Sie können eine beliebige Ausgabe erzeugen.
  • Die Eingabe kann beliebige UTF-8-Zeichen enthalten, einschließlich der Box-Zeichen.

Ausgabe:

  • Alle Zeichen -=|+, die nicht Teil der Tabelle sind, müssen unverändert bleiben.
  • Ebenso müssen alle anderen Zeichen unverändert bleiben.
  • Eine einzelne führende und / oder nachfolgende Zeile ist zulässig.

Andere:

  • Standardlücken sind wie gewohnt verboten.
  • Wenn Ihre bevorzugte Sprache über eine integrierte Sprache verfügt, die dieses Problem löst, können Sie sie möglicherweise nicht verwenden.
    • Dies sind Programme, Funktionen, Unterprogramme oder Anweisungen, die für diese Herausforderung ohne Zusätze gültig wären.
  • Jedes der Zeichen, die für diese Herausforderung benötigt werden, ist drei Byte lang, wenn sie in UTF-8 codiert sind.

Verbundene Charaktere :

Ein Charakter ist mit einem anderen verbunden, wenn:

  • Es ist |und ist direkt über oder unter +oder |;
  • Es ist -und ist direkt vor oder nach +oder -;
  • Es ist =und ist direkt vor oder nach +oder =;
  • Es ist +und direkt über oder unter |oder +, oder direkt vor oder nach -, =oder +.

Ein Zeichen wird als Teil der Tabelle betrachtet, wenn es mit einem Zeichen verbunden ist, das Teil der Tabelle ist. Per Definition ist der erste +in der Eingabe ein Teil der Tabelle.

Beispiele

Beispiele hier als kopierfähige Version verfügbar.

 Input:                    Output:
+------------------+      ┌──────────────────┐
|   Hello+World!   |      │   Hello+World!   │
+==================+      ╞══════════════════╡
| This is+my first |  ->  │ This is+my first │
|+-+ code|golf  +-+|      │+-+ code|golf  +-+│
|+-+chall|enge! +-+|      │+-+chall|enge! +-+│
+------------------+      └──────────────────┘

     +===+===+===+             ╒═══╤═══╤═══╕
     | 1 | 2 | 3 |             │ 1 │ 2 │ 3 │
 +---+===+===+===+         ┌───╪═══╪═══╪═══╡
 | 1 | 1 | 2 | 3 |         │ 1 │ 1 │ 2 │ 3 │
 +---+---+---+---+    ->   ├───┼───┼───┼───┤
 | 2 | 2 | 4 | 6 |         │ 2 │ 2 │ 4 │ 6 │
 +---+---+---+---+         ├───┼───┼───┼───┤
 |-3 |-3 |-6 |-9 |         │-3 │-3 │-6 │-9 │
 +===+---+---+---+         ╘═══╧───┴───┴───┘

      +-----+         ->      <Undefined>

      +-----+         ->      ┌─────┐
      +-----+                 └─────┘

+-----------------+
|  Hello, World!  |
| This is invalid |   ->      <Undefined>
|      input      |
 -----------------+

       ++++                      ┌┬┬┐
       ++++           ->         ├┼┼┤
       ++++                      └┴┴┘

       +--+
       ++++           ->      <Undefined>
       +--+

Schließlich...

Das ist , also gewinnt die geringste Anzahl von Bytes. Viel Spaß beim Golfen!


quelle
Warum werden im ersten Beispiel die aufeinander folgenden +-+Auszüge nicht als zusammenhängende Tabelle betrachtet?
rekursiver
Wenn eine 16-Bit-Funktion ein einzelnes Byte zur Darstellung von ╡ verwendet, wie hoch ist die Byte-Anzahl?
l4m2
@recursive Wenn Sie die erste Hello WorldTabelle meinen, wird davon ausgegangen , dass die inneren Tabellen keine Tabelle bilden, da der Text in der Tabelle unverändert bleiben muss und sie nicht als Teil der äußeren Tabellenränder betrachtet werden, da sie nicht ordnungsgemäß mit ihnen verbunden sind.
Wenn Sie das erste +----+Beispiel meinen , liegt das daran, dass die Richtung der Ecken nicht eindeutig ist.
1
Oh, die Anforderung "Keine Tabellen innerhalb von Tabellen, es sei denn, sie verbinden sich, um die äußerste mögliche Tabelle zu erweitern" macht dies viel schwieriger.
Jonathan Allan

Antworten:

2

Python 3, 392 281 Bytes

Golfen Sie ein bisschen mehr und konvertieren Sie es in eine rekursive Lösung anstatt in eine iterative:

def h(a):
 def g(i):
  k=-3;d=a[i]=='=';z[i]=''
  for p,j,r in zip((1,2,4,8),(i+1,i+w,i-1,i-w),('+-=','+|')*2):
   if 0<=j<len(a)and{a[i],a[j]}<={*r}:k+=p;d|=a[j]=='=';z[j]and g(j)
  z[i]="┌╒!!─═┐╕┬╤@@└╘││├╞┘╛┴╧┤╡┼╪"[2*k+d]
 w=a.find('\n')+1;z=[*a];g(a.find('+'))
 return''.join(z)

Nimmt eine Zeichenfolge von Zeilen gleicher Länge, die durch Zeilenumbrüche getrennt sind, und gibt eine Zeichenfolge im gleichen Format zurück. Kann bei ungültigen Eingaben eine Ausnahme auslösen.

Vorherige Lösung:

def h(a):
 i=a.find('+');q=[i];w=a.find('\n')+1;z=[*a]
 while q:
  i=q.pop();c=a[i];k=-5
  for p,j in enumerate((i+1,i-1,i+w,i-w)):
   r='++-|='[p>1::2]
   if 0<=j<len(a)and a[i]in r and a[j]in r:
    k+=1<<p;q+=[j][:z[j]<'─']
  z[i]='│'if c>'='else'─═'[a[i]>'-']if c>'+'else"┌╒┐╕┬╤@@└╘┘╛┴╧##├╞┤╡┼╪$$"['='in a[abs(i-1):i+2]::2][k]
 return''.join(z)

Ungolfed-Version:

def h(a):
    i = a.find('+')         # find index of first '+'. It is first node
    q = [i]                 # in the queue of indexes to convert to unicode
    w = a.find('\n')+1      # width of the table
    z = [*a]                # strings are immutable, so copy it to a list

    while q:                # while the queue isn't empty
        i=q.pop()           # get the next index to process
        c=a[i]              # and the associated character

        k=-5                # 'k' is the index into the unicode string, U.  The way they
                            # are encoded, the first unicode value is at index 5. 

                 # directions  E   W   S   N
        for p,j in enumerate((i+1,i-1,i+w,i-w)):  # j is the index of an adjacent cell

            # r='++-|='[p>1::2] is equivalent to:
            if p > 1:
                r = '+|'    # compatible symbols for vertical connections
            else:
                r = '+-='   # compatible symbols for horizontal connections

            # if adjacent cell index is valid and the characters are compatible
            if 0 <= j < len(a) and a[i] in r and a[j] in r:
                k += 1<<p                 # update the unicode symbol index

                # q += [j][:z[j]<'─'] is equivalent to:
                if z[j] < '-':            # if the adjacent cell hasn't been converted already
                    q.append(j)           #  append it's index to the queue

            if c > '=':
                z[i] = '│'                # replace a '|' with a '│'

            elif c > '+':
                z[i] = '─═'[a[i]>'-']     # replace a '-' or '=' with '─' or '═' respectively 

            else:                                      # it's a '+'
                U = "┌╒┐╕┬╤@@└╘┘╛┴╧##├╞┤╡┼╪$$"         # even indexes are single horizontal line, 
                                                       # double horizontal lines are at odd indexes

                z[i] = U['='in a[abs(i-1):i+2]::2][k]  # '='in a[abs(i-1):i+2] is true if there is an '=' to the left or right
                                                       # so this selects the odd chars from U
                                                       #  then [k] selects the correct char

 return''.join(z)
RootTwo
quelle
3

Python 3 , 914 898 827 823 594 587 569 540 469 Bytes

Bearbeiten: Deutlich geänderte Strategie, jetzt ein Bitfeld von Nachbarn (ähnlich der Antwort von Dead-Opossum). Ich habe die frühere Version unten gelassen.

H='─│═-|=└╘++++┌╒├╞++┘╛++┴╧┐╕┤╡┬╤┼╪'
def n(l):
 def j(r,c,t=0):O=(0,r-1,c),(1,r,c+1),(2,r+1,c),(3,r,c-1);v=f(r,c);b=t|any(f(Y,X)=='='for i,Y,X in O);l[r][c]={'+':H[b+2*sum((f(Y,X)in H)<<x for x,Y,X in O)],**dict(zip(H[3:6],H))}.get(v,v);[f(Y,X)!=';'and v in'+++-|='[i%2::2]and j(Y,X,v=='=')for i,Y,X in O]
 for i,I in enumerate(l):
  if'+'in I:f=lambda r,c:l[r][c]if len(l)>r>=0and 0<=c<len(l[r])else';';j(i,I.index('+'));break

Probieren Sie es online!

Die Eingabe erfolgt in Form einer Liste von Zeichenlisten, die an Ort und Stelle geändert wird. Rekursionen ab dem ersten gefundenen.

x=range
C='┌┐└┘','╒╕╘╛'
D='┬┤┴├','╤╡╧╞'
A='┼╪'
H,V,T='─│═'
J={'-':H,'|':V,'=':T}
K=C[1]+D[1]+A[1]+'='+T
E=('+|','+-=')*2
F=['+|'+V,'+-='+H+T]*2
O=(0,-1,0),(1,0,1),(2,1,0),(3,0,-1)
for i in x(4):
 for j in{0,1,2,3}-{i}:F[i+2&3]+=D[0][j]+D[1][j]
 h=C[0][i]+C[1][i];F[i&2]+=h;F[3-2*(i&1)]+=h
def n(l):
 for i,I in enumerate(l):
  if'+'in I:r=i;c=I.index('+');break
 else:return l
 def f(r,c):
  try:assert c>=0 and r>=0;return l[r][c]
  except:return'\0'
 def j(r,c):
  v=f(r,c)
  l[r][c]=J.get(v,v)
  if v=='+':
   X=[f(r+Y,c+X)for i,Y,X in O];B=any(x in K for x in X);X=[X[x]in F[x]for x in x(4)];L=sum(X)
   if L in(2,3,4):l[r][c]=D[B][X.index(False)]if L==3 else C[B][X[0]*2+X[3]]if L==2 else A[B]
  for i,Y,X in O:
   if v in E[i]and f(r+Y,c+X)in E[i]:j(r+Y,c+X)
 j(r,c);return l

Probieren Sie es online!

Hier ist die nächste Sache, die ich zu einer ungolfed Version habe:

def tr(s):
    t='┌┐└┘','╒╕╘╛'
    t2='┬┤┴├','╤╡╧╞'
    A = '┼','╪'
    H,V,T = '─│═'
    Th = ''.join(x[1]for x in (t,t2,A))+'='+T
    ps = ['+|'+V, '+-='+H+T, '+|'+V, '+-='+H+T]
    ps2 = ('+|', '+-=')*2
    for i in range(4):
        for j in {0,1,2,3}-{i}:
            ps[(i+2)%4] += t2[0][j]+t2[1][j]
        h=t[0][i] + t[1][i]
        ps[i & 2] += h
        ps[3 - 2 * (i & 1)] += h

    l = [list(x) for x in s.split('\n')]
    r = 0
    for i,I in enumerate(l):
        if'+'in I:
            r=i;c=I.index('+')
            break
    def g(r,c): return l[r][c]
    def G(r,c):
        if r >= 0 and r < len(l) and c >= 0 and c < len(l[r]):
            return g(r,c)
        return '\0'
    def process(r,c):
        v = g(r,c)
        if v == '-': l[r][c] = H
        elif v == '|': l[r][c] = V
        elif v == '=': l[r][c] = T
        elif v == '+':
            all=[G(r-1,c),G(r,c+1),G(r+1,c),G(r,c-1)]
            bold=any(x in Th for x in all)
            for i in range(4):all[i] = all[i] in ps[i]
            N,E,S,W=all
            tt=sum(all)
            if tt == 3:
                l[r][c]=t2[bold][all.index(False)]
            elif tt == 2:
                l[r][c]=t[bold][N*2+W]
            elif tt == 4:
                l[r][c]=A[bold]
            else: return
        for i,(dy,dx) in enumerate(((-1,0),(0,1),(1,0),(0,-1))):
            if v in ps2[i] and G(r+dy,c+dx) in ps2[i]:
                process(r+dy,c+dx)
    process(r,c)
    return l
pbfy0
quelle
Kleinere Verbesserungen zum Speichern von 9 Bytes (bis zu 814) bit.ly/2NOu7HF
mypetlion
Einige mehr für weitere 9 Bytes (jetzt 805 Bytes) bit.ly/2pYom0x
mypetlion
Bis 763: bit.ly/2OxErsJ
mypetlion
1

JavaScript, 311 307 Bytes

X=>(O=[...X],P=(I,j=0,_=0)=>!P[I]&&(P[I]=1,['-─1','|│','=═1'].map(([a,b,n=X.indexOf('\n')+1])=>[-n,+n].map(n=>{for(i=I;X[i+=n]==a;)O[i]=b
if(X[i]=='+')j|=[1,2,4,8,I-i>1&&17,i-I>1&&18][_],P(i)
_++})),O[I]='┘└┴ ┐┌┬ ┤├┼     ╛╘╧ ╕╒╤ ╡╞╪'[j-5]),P(X.indexOf`+`),O.join``)

Erläuterung

Ab der ersten gefundenen +Kreuzung versucht das Programm, Pfade zu anderen Kreuzungen in alle Richtungen zu finden, und führt dabei Ersetzungen durch. Es speichert die gefundenen Richtungen und den "doppelten Rand" -Zustand in einer Bitmap, die dann das entsprechende Kreuzungszeichen bestimmt.

// Take an input string X
f = X => {
    // Copy the input string into an array so characters can be overwritten and eventually output
    O = [...X]

    // Define a function that processes a junction ("+" symbol) at index I in the input string X:
    P = I => {
        // Make a bitmap to keep track of the direction coming out of the junction and double borders
        // Bits from right to left: west, east, north, south, double border
        // E.g. a double-bordered south/east junction corresponds to the binary number 11010 ("╒")
        let j = 0

        // A counter
        let _ = 0

        // Ensure this junction hasn't already been processed
        if(!P[I]){
            P[I] = 1,

            // We'll walk away from the junction in each of the four directions, then west and east again to check for double borders
            // i.e. walk along `a`, replace with `b`, move index `i` by `n`
            // 1st pass: walk along "-", replace with "─", move index by 1
            // 2nd pass: walk along "|", replace with "│", move index by the width of the input (plus 1 for the newline) to traverse vertically
            // 3rd pass: walk along "=", replace with "═", move index by 1
            ['-─1','|│','=═1'].map(([a, b, n = X.indexOf('\n') + 1])=>
                // We'll walk in the negative and positive directions for each pass
                [-n,+n].map(n=>{
                    // Start the walk
                    i=I
                    // Keep walking (incrementing by n) as long as we're on a "path" character, "a"
                    while(i += n, X[i] == a)
                        // Replace the corresponding character in the output with "b"
                        O[i] = b

                    // Upon reaching another junction at index i:
                    if(X[i] == '+'){
                        // OR the bitmap according to the direction we walked
                        j |= [
                            // Pass 1: Horizontal
                            1, // west
                            2, // east

                            // Pass 2: Vertical
                            4, // north
                            8, // south

                            // Pass 3: Double Horizontal (only if we've walked more than 1 step)
                            I-i > 1 && 17, // west, double border
                            i-I > 1 && 18 // east, double border
                        ][_]

                        // Process the junction we walked to
                        P(i)
                    }
                    _++
                })
            )

            // Finally, replace the "+" with a proper junction character based on the bitmap value
            O[I] = '     ┘└┴ ┐┌┬ ┤├┼     ╛╘╧ ╕╒╤ ╡╞╪'[j]
        }
    }

    // Process the first junction to kick off the recursion
    P(X.indexOf`+`)

    // Return our modified character array as a joined string
    return O.join``
}
darrylyeo
quelle
Behoben - Ich muss mir die Anzahl der Zeichen und nicht die Anzahl der Bytes angesehen haben.
Darrylyeo
1

Python 3 , 599 Bytes

Ich kann in Python 3 nicht wirklich gut Golf spielen, aber (zu meiner Schande) konnte ich in Python 2 keine normale Ausgabe von UTF-8-Zeichen erhalten.

Ich denke, der einzige interessante Trick hier ist die Entscheidung über die +Transformation.
Ich habe alle möglichen Varianten mit 4-Bit-Adressen verschlüsselt. Jedes Adressbit ähnelt der Verbindung zur Nachbarzelle. Also 0 - keine Verbindung und 1 - Verbindung.
1111ist
0011ist
usw.
Einige Konfigurationen von Verbindungen sind ungültig und ersetzt mit Dummy - Werten:'012┐45┌┬8┘0┤└┴├┼'

Wenn eine Nachbarzelle enthält =, wird die zweite Liste mit doppelten Zeilen verwendet.

['012┐45┌┬8┘0┤└┴├┼','012╕45╒╤8╛0╡╘╧╞╪']['='in r]

Die Adresse wird hier zusammengefasst.

r=''.join([str(int(V(y,x)))+W(y,x)for y,x in[(Y-1,X),(Y,X+1),(Y+1,X),(Y,X-1)]])

r enthält Stringlänge 8, wobei alle zwei Zeichen 1/0 sind und das tatsächliche Nachthauptzeichen.
Zum Beispiel: 1+0y1-1|.
Dies wird verwendet, um die Liste der Ersetzungen wie oben gezeigt auszuwählen. Und dann beauftragt zu adressieren:int(r[0::2],2)

Dieses Lambda wird verwendet, um zu überprüfen, ob die Zellenkoordinaten gültig sind und das Zeichen der Zelle eines von '+ - | =' ist.

V=lambda y,x:~0<x<len(I[0])and~0<y<len(I)and I[y][x]in'+-|='

Dieses Lambda wurde verwendet, um Saibling aus der Zelle zu erhalten. Kehrt zurück' ' wenn die Koordinaten ungültig sind. (kann auf jeden Fall weggolfen werden)

W=lambda y,x:V(y,x)and I[y][x]or' '

Bedingungen für die Rekursion. Könnte auch Golf sein.

if Z in'+-=':F(Y,X+1);F(Y,X-1)
if Z in'+|':F(Y-1,X);F(Y+1,X)

I=eval(input())
J=[i[:]for i in I]
V=lambda y,x:~0<x<len(I[0])and~0<y<len(I)and I[y][x]in'+-|='
W=lambda y,x:V(y,x)and I[y][x]or' '
def F(Y,X):
 if V(Y,X)and I[Y][X]==J[Y][X]:
  Z=I[Y][X]
  if','>Z:
   r=''.join([str(int(V(y,x)))+W(y,x)for y,x in[(Y-1,X),(Y,X+1),(Y+1,X),(Y,X-1)]])
   J[Y][X]=['012┐45┌┬8┘0┤└┴├┼','012╕45╒╤8╛0╡╘╧╞╪']['='in r][int(r[0::2],2)]
  else:J[Y][X]=dict(zip('|-=','│─═'))[Z]
  if Z in'+-=':F(Y,X+1);F(Y,X-1)
  if Z in'+|':F(Y-1,X);F(Y+1,X)
e=enumerate
F(*[(y,x)for y,r in e(I)for x,c in e(r)if'+'==c][0])
for r in J:print(''.join(r))

Probieren Sie es online!

Totes Opossum
quelle