g | x | w | all
Bytes Lang Time Link
088Uiua250408T112910Znoodle p
524AWK241205T182706Zxrs
609Scala 3240515T091830Z138 Aspe
259JavaScript Node.js220426T015448Znoodle p
588C gcc220616T160512Zevanstar
261JavaScript220421T033803ZMatthew
238PowerShell for Windows220423T074627Zmazzy
025APL Dyalog Extended180619T191555ZAdá
043Try Dyalog APL161121T141328ZAdá
281JavaScript ES6161129T074956Zedc65
365PowerShell 3+161121T215807Zbriantis
313PHP161128T163152ZTitus
286JavaScript ES6|FireFox161123T204513ZMwr247
578Racket161125T041450Zrnso
282Perl161124T172954ZDenis Ib
696C#161124T012158ZPete Ard
318Python 3161121T135938ZKarl Nap

Uiua, 89 88 bytes

⍜⊣˜⊂⍜⊢⊟≡˜⊂+4_8_4⟜≡⊂-32⟜≡/◇(⊂⊂)"┬┼┴"¤⍚↯⊙@─≡◇⧻⍚⊢⟜(°⊂≡⊂≡⊂⟜⤚/◇≡(⊂⊂)@│)⍚⬚@ ≡°□⍉°csv

Try it: Uiua pad

(The 88 bytes is when encoded with a byte for everything in ASCII + Uiua's glyphs, and an extra byte added to the UTF-16 representation for everything else. It's possible to reduce the character count but it would increase the byte count.)

AWK, 524 bytes

func L(A,C,D,E,F,G){f(A)f(H="─",C)f(D)f(H,E)f(D)f(H,F)f(G RS)}func f(x,y){for(y=y?y:1;y--;)printf x}{x++;for(i=0;i++<NF;)if(l[i]<(t=length($i)))l[i]=t+1;for(i=0;i++<NF;)a[x][i]=$i}BEGIN{FS=","}END{V="│"
L("┌",l[1],"┬",l[2],l[3],"┐")f(V)f(a[1][1])f(" ",l[1]-length(a[1][1]))f(V)f(a[1][2])f(" ",l[2]-length(a[1][2]))f(V)f(a[1][3])f(" ",l[3]-length(a[1][3]))f(V RS)L("├",l[1],"┼",l[2],l[3],"┤")
for(j=1;j++<x;print"|")for(k=0;k++<NF;)f(V a[j][k])f(" ",l[k]-length(a[j][k]))
L("└",l[1],"┴",l[2],l[3],"┘")}

Attempt This Online!

It's heavy but works. I might come back to it in the future with fresh eyes.

Scala 3, 609 bytes

609 bytes, it can be golfed more.


Golfed version. Attempt This Online!

T=>{
val t=T.split("\n").map(_.split(","))
val h=t(0)
val d=t.tail
val w=h.indices.map(i=>t.map(_(i).length).max).toArray
def l(x:Array[String],i:Option[Int])={
val b=i.isEmpty
val(a,b1,c,p)=if(b){
x(0)match {
case "┌"=>("┌","┬","┐",'─')
case "├"=>("├","┼","┤",'─')
case "└"=>("└","┴","┘",'─')
}
}else{
("│","│","│",' ')
}
a+w.zipWithIndex.map{case(n,j)=>
(if(p==' ')x(j)else"").padTo(n,p)
}.mkString(b1)+c+"\n"
}
val tB=l(Array("┌"),None)
val hL=l(h,Some(0))
val mB=l(Array("├"),None)
val dL=d.map(r=>l(r,Some(1))).mkString
val bB=l(Array("└"),None)
tB+hL+mB+dL+bB
}

Ungolfed version. Attempt This Online!

object Main extends App {
  def formatTable(tableCSV: String): String = {
    val table = tableCSV.split("\n").map(_.split(","))
    val header = table.head
    val data = table.tail
    val widths = header.indices.map(i => table.map(row => row(i).length).max).toArray

    def drawLine(x: Array[String], i: Option[Int]): String = {
      val isBorder = i.isEmpty
      val (a, b, c, pad) = if (isBorder) {
        x.head match {
          case "┌" => ("┌", "┬", "┐", '─')
          case "├" => ("├", "┼", "┤", '─')
          case "└" => ("└", "┴", "┘", '─')
        }
      } else {
        ("│", "│", "│", ' ')
      }
      a + widths.zipWithIndex.map { case (n, j) =>
        (if (pad == ' ') x(j) else "").padTo(n, pad)
      }.mkString(b) + c + "\n"
    }

    val topBorder = drawLine(Array("┌"), None)
    val headerLine = drawLine(header, Some(0))
    val middleBorder = drawLine(Array("├"), None)
    val dataLines = data.map(row => drawLine(row, Some(1))).mkString
    val bottomBorder = drawLine(Array("└"), None)

    topBorder + headerLine + middleBorder + dataLines + bottomBorder
  }

  val tableCSV = """
Name,Age,Gender
Shaun,19,Male
Debra,19,Female
Alan,26,Male
George,15,Male
""".trim

  println(formatTable(tableCSV))
}

JavaScript (Node.js), 291 259 bytes

(s,[h,...r]=s.split`
`.map(l=>l.split`,`.map((z,i)=>(b[i]=Math.max(b[i]|0,z.length),z)),b=[]).map(l=>(t="│")+l.map((z,i)=>z.padEnd(b[i])).join(t)+t),d=c=>b.map(n=>"─".repeat(n)).join(c))=>`┌${d`┬`}┐
${h}
├${d`┼`}┤
${r.join`
`}
└${d`┴`}┘`

Attempt This Online!

2 bytes shorter than Jensen’s :)

C (gcc), 588 bytes

#define H char
P(c,n){for(H*s[]={"\n","┌","┬","┐","├","┼","┤","└","┴","┘","─","│"};n--;)printf("%s",s[c]);}S(s,n)H*s;{printf("%-*s",n,s);}E(n,c,Z,i)int*Z;{for(i=P(3*n+1,1);i<c;P(3*n+(++i-c?2:3),1))P(10,Z[i]);P(0,1);}V(t,c,Z,r,i)H**t;int*Z;{for(i=P(11,1);i<c;i++,P(11,1))S(t[i+r],Z[i]);P(0,1);}f(s,p)H*s,*p;{int r,R,C,i,m,L;R=C=i=m=r=0;for(p=s;*p;*p++==44&&C++)!(p[1]&&*p^10)&&R++;C/=R;H*t[R*++C];for(p=s;i<R*C;*p++=0)for(t[i++]=p;*p^44&&*p^10;)p++;int Z[C];for(i=0;i<C;Z[i++]=m)for(m=r=0;r<R;m=m<L?L:m)L=strlen(t[i+C*r++]);E(0,C,Z);V(t,C,Z,0);for(E(i=1,C,Z);i<R;)V(t,C,Z,C*i++);E(2,C,Z);}

Try it online!

JavaScript, 261 bytes

(t,[H,...D]=T=t.split`
`.map(r=>r.split`,`),w=H.map((_,i)=>Math.max(...T.map(r=>r[i].length))),L=(x,i,_,[[[a,b=a,c=a]],p]=1/i?`│`:[x,'─'])=>a+w.map((n,j)=>(p?'':x[j]).padEnd(n,p)).join(b)+c+`
`)=>L`┌┬┐`+L(H,0)+L`├┼┤`+D.map(L).join``+L`└┴┘`

Try it online!

Ungolfed and explained

(
  tableCSV,
  [header, ...data] = table = tableCSV.split(`\n`).map(row => row.split(`,`)),
  widths = header.map((_, i) => Math.max(...table.map(row => row[i].length))),
  drawLine = ( // create data row or border
    x, // either data array or singleton string array
    i, // either data index or undefined
    _, // ignored (data.map(drawLine) sets this to data)
    [[[a, b = a, c = a]], pad] = 1 / i ? `│` : [x, '─']
       // a = start, b = separator, c = end, pad = '─' if making border
  ) =>
    a +
    widths.map((n, j) => (pad ? '' : x[j]).padEnd(n, pad)).join(b) +
    c +
    `\n`
) =>
  drawLine`┌┬┐` +
  drawLine(header, 0) +
  drawLine`├┼┤` +
  data.map(drawLine).join(``) +
  drawLine`└┴┘`

PowerShell for Windows, 256...236 238 bytes

$v=$args-split'
'-ne''|%{,($_-split',')}
function z($b,$a){$b[1]+(($v[0]|%{@($a)[$x++]+''|% p*ht($v|%{$_[$x-1]}|% le*|sort -b 1)$b[0]})-join$b[2])+$b[3]}
z '─┌┬┐'
$v|%{z ' │││'$_;if(!$r++){z '─├┼┤'}}
z '─└┴┘'

Try it online!

Notes:

An input csv contains:

APL (Dyalog Extended), 36 25 bytesSBCS

Full program. Assumes that ABCDEFGHIJKLMNOPQRSTUVWXYZ is the CSV file. Prints to stdout.

⌂disp(1↑m)⍪↑¨↓⍉1↓m←⎕CSV⎕A

Try it online!

⎕A the uppercase Alphabet (the shortest-to-reference built-in string)
⎕CSV read that file and convert from CSV to matrix
m← store as m (for matrix)
1↓ drop the first row
 transpose
 split into list of columns
↑¨ mix each list of strings into a matrix
()⍪ stack the following on top of that:
1↑m take the first row of m
⌂disp apply dfns.disp to that (draws line drawing characters)

Try (Dyalog) APL, 38 43 bytes

Last input line must have a trailing newline.

{{(⊃⍵)⍪⍉⍪↑¨↓⍉↑1↓⍵}s¨',',¨(s←1↓¨⊢⊂⍨⊢=⊃)¯1⌽⍵}

Try it online! In the offline version of Dyalog APL, execute ]boxing ON -style=min for the same effect.

Explanation

{ ... } an anonymous function where represents the argument:

¯1 ⌽ ⍵ rotate the trailing newline to the front

(s ← ... ) define the function s as follows, and apply it

  1 ↓¨ drop the first character of each

  ⊢ ⊂⍨ line, split where

  ⊃ = ⊢ the first character equals the characters in the string

',' ,¨ then prepend a comma to each line

apply the function s to each line

{ ... } now apply the following anonymous function:

  1 ↓ ⍵ drop the first element (the row headers)

  ↓ ⍉ ↑ transpose the list of rows into list of columns

  ↑¨ make each element (a list of entries) into a matrix of padded entries

  ⍉ ⍪ make into one-column matrix, then transpose into one-row matrix

  (⊃⍵) ⍪ put the argument's first element (the list of headers) on top`

Note: While the line drawing characters are not explicitly used in my solution, they are part of the APL character set, and would also be counted as single bytes.

JavaScript (ES6), 281 bytes

Note: input as a single string with newlines - as requested by OP. Other answers use a string list - using a string array in input I can avoid the first split and cut 9 bytes.

l=>(l=l.split`
`.map(r=>r.split`,`.map((w,i)=>(v=w.length)<c[i]?w:(c[i]=v,w)),c=[k=0]),l=l.map(r=>r.map((v,i)=>(v+' '.repeat(c[i]-v.length)))),[h=c.map(x=>'─'.repeat(x)),l.shift(),h,...l,h].map(a=>'│┌├└'[j=a!=h?0:++k]+a.join('│┬┼┴'[j])+'│┐┤┘'[j]).join`
`)

Less golfed

l=>(
  // split input in an array of string arrays
  // meanwhile find the column widths and put them in *c*
  l = l.split`\n`.map(r=>r.split`,`.map((w,i)=>(v=w.length)<c[i]?w:(c[i]=v,w)),c=[]),

  // pad each column to the max column width
  l = l.map(r=>r.map((v,i)=>(v+' '.repeat(c[i]-v.length)))),

  // put in *h* the horizontal lines for top,bottom and head separator
  h = c.map(x => '─'.repeat(x) ),

  // add the *h* line at top, bottom and after head line
  l = [h, l.shift(), h, ...l, h],

  // rebuild a string, joining columns with '|' unless the row is *h*
  // if the row is *h* use different characters to join columns
  k = 0, 
  l.map(a=> '│┌├└'[j=a!=h?0:++k] + a.join('│┬┼┴'[j]) + '│┐┤┘'[j])
  .join`\n`  
)

Test

F=
l=>(l=l.split`
`.map(r=>r.split`,`.map((w,i)=>(v=w.length)<c[i]?w:(c[i]=v,w)),c=[k=0]),l=l.map(r=>r.map((v,i)=>(v+' '.repeat(c[i]-v.length)))),[h=c.map(x=>'─'.repeat(x)),l.shift(),h,...l,h].map(a=>'│┌├└'[j=a!=h?0:++k]+a.join('│┬┼┴'[j])+'│┐┤┘'[j]).join`
`) 
  
function update() {
  O.textContent = F(I.value)
}
update()
#I { width:60%; height: 8em} 
<textarea id=I>Name,Age,Gender
Shaun,19,Male
Debra,19,Female
Alan,26,Male
George,15,Male</textarea><br>
<button onclick='update()'>Go</button>
<pre id=O></pre>

PowerShell 3+, 365 bytes

$d=$input|ipcsv
$h=$d[0].PSObject.Properties.Name|%{$_|Add-Member -type NoteProperty -na c -v(($d.$_+$_|measure Length -ma).Maximum)-pa}
"┌$(($h|%{'─'*$_.c})-join'┬')┐"
"│$(($h|%{$_.PadRight($_.c)})-join'│')│"
"├$(($h|%{'─'*$_.c})-join'┼')┤"
$d|%{$i=$_;"│$(($h|%{$i.$_.PadRight($_.c)})-join'│')│"}
"└$(($h|%{'─'*$_.c})-join'┴')┘"

I feel like this could be improved a lot but I ran out of time. All line endings are \n with no \r, encoding is UTF8 with no BOM.

PHP, 313 bytes

for(;$r=fgetcsv(STDIN);$a[]=$r)foreach($r as$x=>$s)$e[$x]=max($e[$x],strlen($s));$t=["┬","┌","┐"];eval($L='foreach($e as$i=>$n)echo$t[!$i],str_repeat("─",$n);echo"$t[2]\n";');foreach($a as$k=>$r){foreach($r as$i=>$s)echo"│",str_pad($s,$e[$i]);echo"│\n";$t=["┼","├","┤"];if(!$k)eval($L);}$t=["┴","└","┘"];eval($L);

breakdown

for(;$r=fgetcsv(STDIN);$a[]=$r)                         // read csv from STDIN, append to array $a
    foreach($r as$x=>$s)$e[$x]=max($e[$x],strlen($s));  // remember max length in array $e
                                                        // print top border
$t=["┬","┌","┐"];eval($L='foreach($e as$i=>$n)echo$t[!$i],str_repeat("─",$n);echo"$t[2]\n";');
foreach($a as$k=>$r)
{
    foreach($r as$i=>$s)echo"│",str_pad($s,$e[$i]);echo"│\n";   // print row
    $t=["┼","├","┤"];if(!$k)eval($L);                           // print border below header
}
$t=["┴","└","┘"];eval($L);                              // print bottom border

Test it at ideone

JavaScript (ES6|FireFox), 286 bytes

f=>(d=f.split`
`.map(a=>a.split`,`),s=d[0].map((a,i)=>d.reduce((b,c)=>(n=c[i].length)>b?n:b,0)),d=d.map(a=>`│${a.map((b,i)=>b.padEnd(s[i])).join`│`}│`),d.splice(1,0,(g=h=>h[0]+s.map(a=>'─'.repeat(a)).join(h[1])+h[2])('├┼┤')),g('┌┬┐')+`
${d.join`
`}
`+g('└┴┘'))

Uses padEnd, which is FireFox specific.

Racket 578 bytes

(let*((ll(map(λ(x)(string-split x","))ll))(lr list-ref)(sl string-length)(d display)(dl displayln)(nc(length(lr ll 0)))
(nl(for/list((i nc))(apply max(for/list((j ll))(sl(lr j i))))))(pl(λ(sy)(d(lr sy 0))(for((n nc))(for((m(lr nl n)))(d(lr sy 1)))
(if(< n(sub1 nc))(d(lr sy 2))(dl(lr sy 3))))))(g(λ(i n)(for((m(-(lr nl n)(sl i))))(d" ")))))(pl'("┌""─""┬""┐"))
(for((i(lr ll 0))(n(in-naturals)))(d"│")(d i)(g i n))(dl"│")(pl'("├""─""┼""┤"))(for((j(range 1(length ll))))
(for((i(lr ll j))(n nc))(d"│")(d i)(g i n))(dl"│"))(pl'("└" "─" "┴" "┘")))

Ungolfed:

(define(f1 ll)
 (let* ((ll (map (λ (x)(string-split x ",")) ll))  ; use this to convert csv format to list of lists; 
         (lr list-ref)                    ; make short names of standard fns
         (sl string-length)
         (d display)
         (dl displayln)
         (nc (length (lr ll 0)))          ; number of cols; 
         (nl(for/list ((i nc))            ; get list of max string-length for each column
              (apply max
                     (for/list ((j ll))
                       (sl (lr j i))
                       ))))
         (pl (λ (sy)                      ; put lines using sent symbol list
               (d (lr sy 0)) 
               (for ((n nc))
                 (for ((m (lr nl n))) (d (lr sy 1)))
                 (if (< n (sub1 nc))
                     (d (lr sy 2))
                     (dl (lr sy 3))
                     ))))
         (g (λ (i n)                     ; pad with spaces if needed
              (for ((m (- (lr nl n) (sl i)))) (d " ")) ))) 
    ; put line above header: 
    (pl '("┌" "─" "┬" "┐"))

    ; put header: 
    (for ((i (lr ll 0)) (n (in-naturals)))
      (d "│")
      (d i)
      (g i n)
      )
    (dl "│")

    ; put line below header;
    (pl '("├" "─" "┼" "┤"))

    ; put rows: 
    (for ((j (range 1 (length ll))))
      (for ((i (lr ll j))
            (n nc))
        (d "│")
        (d i)
        (g i n)
        )
      (dl "│")
      )

    ; put bottom line: 
    (pl '("└" "─" "┴" "┘"))
    ))

Testing:

(f (list  "Name,Age,Gender"
          "Shaun,19,Male"
          "Debra,19,Female"
          "Alan,26,Male"
          "George,15,Male"))

Output:

┌──────┬───┬──────┐
│Name  │Age│Gender│
├──────┼───┼──────┤
│Shaun │19 │Male  │
│Debra │19 │Female│
│Alan  │26 │Male  │
│George│15 │Male  │
└──────┴───┴──────┘

Perl, 273 + 9 (-CS -nlaF, flags) = 282 bytes

$v[$.-1]=[@F];map$l[$_]<($l=length$F[$_])&&($l[$_]=$l),0..$#F}sub p{printf$p,@_}sub o{p
pop,map{$\x$l[$_],$_-$#l?$_[0]:pop}0..$#l}$p=join'%s','',(map"\%-${_}s",@l),$/;($\,$c,@c)=map
chr$_*4+9472,0,.5,3..15;o@c[8,1,0];p($c,map{$_,$c}@$_),$i++||o@c[12,6,4]for@v;o@c[10,3,2];{

Using:

cat file.csv | perl -CS -nlaF, script.pl

Try it on Ideone.

C#, 696 Bytes

Golfed:

string T(string[]f){int w=f.Max(r=>r.Length),a=f.Select(r=>r.Split(',')[0].Length).Max(),b=f.Select(r=>r.Split(',')[1].Length).Max(),c=f.Select(r=>r.Split(',')[2].Length).Max();string o="",n="\r\n",d="",j=string.Concat(Enumerable.Repeat("─",a)),k=string.Concat(Enumerable.Repeat("─",b)),l=string.Concat(Enumerable.Repeat("─",c));Func<string,int,string>z=(q,p)=>{return q.PadRight(p);};d="┌"+j+"┬"+k+"┬"+l+"┐";o+=d+n;var g=f.First().Split(',');o+="|"+z(g[0],a)+"|"+z(g[1],b)+"|"+z(g[2],c)+"|";d="├"+j+"┼"+k+"┼"+l+"┤";o+=n+d+n;for(int i=1;i<f.Length;i++){var h=f[i].Split(',');o+="|"+z(h[0],a)+"|"+z(h[1],b)+"|"+z(h[2],c)+"|"+n;}d="└"+j+"┴"+k+"┴"+l+"┘";o+=d;return o;}

Ungolfed (and nicer, because ^that is no use to anyone):

public string T(string[] c)
{
  int width = c.Max(r => r.Length),
    longestFirstColumn = c.Select(r => r.Split(',')[0].Length).Max(),
    longestSecondColumn = c.Select(r => r.Split(',')[1].Length).Max(),
    longestThirdColumn = c.Select(r => r.Split(',')[2].Length).Max();

  string o = "", lr = "\r\n", border = "",
    firstColumnFiller = string.Concat(Enumerable.Repeat("─", longestFirstColumn)),
    secondColumnFiller = string.Concat(Enumerable.Repeat("─", longestSecondColumn)),
    thirdColumnFiller = string.Concat(Enumerable.Repeat("─", longestThirdColumn));

  Func<string, int, string> padRight = (a, b) => { return a.PadRight(b); };

  border = "┌" + firstColumnFiller
    + "┬" +
    secondColumnFiller + "┬"
    + thirdColumnFiller
    + "┐";

  o += border + lr;

  var firstRow = c.First().Split(',');

  o += "|" + padRight(firstRow[0], longestFirstColumn) +
    "|" + padRight(firstRow[1], longestSecondColumn) +
    "|" + padRight(firstRow[2], longestThirdColumn) + "|";

  border = "├" +
    firstColumnFiller + "┼" +
    secondColumnFiller + "┼" +
    thirdColumnFiller
    + "┤";

  o += lr + border + lr;

  for (int i = 1; i < c.Length; i++)
  {
    var row = c[i].Split(',');

    o += "|" + padRight(row[0], longestFirstColumn) + "|"
    + padRight(row[1], longestSecondColumn) + "|" +
    padRight(row[2], longestThirdColumn) + "|" + lr;
  }

  border = "└" +
    firstColumnFiller + "┴" +
    secondColumnFiller + "┴" +
    thirdColumnFiller
    + "┘";

  o += border;

  return o;
}

Testing:

┌──────┬───┬──────┐         ┌──────────┬───────────────────────────┬─────┐
|Name  |Age|Gender|         |Name      |PPCG Challenge             |Votes|
├──────┼───┼──────┤         ├──────────┼───────────────────────────┼─────┤
|Shaun |19 |Male  |         |Pete Arden| Print all integers        | 4   |
|Debra |19 |Female|         |Pete Arden| Yes of course I'm an adult| 3   |
|Alan  |26 |Male  |         |Pete Arden| 5 Favorite Letters        | 1   |
|George|15 |Male  |         └──────────┴───────────────────────────┴─────┘
└──────┴───┴──────┘

Python 3, 318 bytes

-3 bytes for using the % formatting and -1 for abbreviating str.join

L=[c.split(',')for c in input().split('\n')]
m=[max(len(x)for x in c)for c in zip(*L)]
L=[[""]+[d.ljust(n)for d,n in zip(c,m)]+[""]for c in L]
g=["─"*i for i in m]
J=str.join
print('\n'.join(["┌%s┐"%J("┬",g),J("│",L[0]),"├%s┤"%J("┼",g)]+[J("│",L[i])for i in range(1,len(L))]+["└%s┘"%J("┴",g)]))

Requires input enclosed in quotes.