Text-CSV_XS

 view release on metacpan or  search on metacpan

examples/csvdiff  view on Meta::CPAN

#!/pro/bin/perl

use 5.012000;
use warnings;

# csvdiff: Show differences between CSV files
#	   (m)'23 [05 Aug 2023] Copyright H.M.Brand 2009-2025

our $VERSION = "1.03 - 20230805";

sub usage {
    my $err = shift and select STDERR;
    print "usage: csvdiff [--no-color] [--html] [-w|-b|-Z] file.csv file.csv\n",
	"  provides colorized diff on sorted CSV files\n",
	"  assuming first line is header and first field is the key\n",
	"          --no-color               do not use colors\n",
	"    -h    --html                   produce HTML output\n",
	"    -w    --ignore-all-space       ignore all white space\n",
	"    -b    --ignore-space-change    ignore changes in the amount of white space\n",
	"    -Z    --ignore-trailing-space  ignore white space at line end\n",
	"    -o F  --output=F               send output to file F\n";
    exit $err;
    } # usage

use Getopt::Long qw(:config bundling nopermute );
my $opt_c = !$ENV{NO_COLOR};
GetOptions (
    "help|?"		=> sub { usage (0); },
    "V|version"		=> sub { print "csvdiff [$VERSION]\n"; exit 0 },

    "w|ignore-all-space!"		=> \my $opt_w,
    "b|ignore-ws|ignore-space-change!"	=> \my $opt_b,
    "Z|ignore-trailing-space!"		=> \my $opt_Z,

    "c|color|colour!"			=> \   $opt_c,
    "h|html"				=> \my $opt_h,

    "o|output=s"			=> \my $opt_o,
    ) or usage (1);

@ARGV == 2 or usage (1);

if ($opt_o) {
    open STDOUT, ">", $opt_o or die "$opt_o: $!\n";
    }

use HTML::Entities;
use Term::ANSIColor qw(:constants);
use Text::CSV_XS;
my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 0 });

if ($opt_h) {
    binmode STDOUT, ":encoding(utf-8)";
    my $name = $^O eq "MSWin32" ? Win32::LoginName () : scalar getpwuid $<;
    print <<"EOH";
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <title>CFI School updates</title>
  <meta name="Generator"     content="perl $]" />
  <meta name="Author"        content="$name" />
  <meta name="Description"   content="CSV diff @ARGV" />
  <style type="text/css">
    .rd { background:	#ffe0e0;	}
    .gr { background:	#e0ffe0;	}
    .hd { background:	#e0e0ff;	}
    .b0 { background:	#e0e0e0;	}
    .b1 { background:	#f0f0f0;	}
    .r  { color:	red;		}
    .g  { color:	green;		}
    </style>
  </head>
<body>

<h1>CSV diff @ARGV</h1>

<table>
EOH
    $::{RED}	= sub { "\cA\rr";	};
    $::{GREEN}	= sub { "\cA\rg";	};
    $::{RESET}	= sub { "";		};
    }
elsif (!$opt_c) {
    $::{$_} = sub { "" } for qw( RED GREEN RESET );
    }

my @f;
my $opt_n = 1;
foreach my $x (0, 1) {
    open my $fh, "<", $ARGV[$x] or die "$ARGV[$x]: $!\n";
    my $n = 0;
    while (1) {
	my $row = $csv->getline ($fh) or last;
	@$row and push @{$f[$x]}, $row;
	$n++ && $row->[0] =~ m/\D/ and $opt_n = 0;
	}
    }
my @n   = map { $#{$f[$_]} } 0, 1;
my @i   = (1, 1);
my $hdr = "# csvdiff   < $ARGV[0]    > $ARGV[1]\n";

$f[$_][1+$n[$_]][0] = $opt_n ? 0x7FFFFFFF : "\xff\xff\xff\xff" for 0, 1;

my %cls;
   %cls = (
    "b" => 0,
    "-"	=> sub { "rd" },
    "+"	=> sub { "gr" },
    "H"	=> sub { "hd" },
    "<"	=> sub { $cls{b} ^= 1; "b$cls{b}" },
    ">"	=> sub { "b$cls{b}" },
    );



( run in 1.105 second using v1.01-cache-2.11-cpan-39bf76dae61 )