Download tools/mosesdecoder/scripts/tokenizer/detokenizer.perl from Quagmire1/wiki-cased: direct link, hf CLI and curl.
- Browser
- Download file 12.5 kB
-
https://huggingface.co/Quagmire1/wiki-cased/resolve/main/tools/mosesdecoder/scripts/tokenizer/detokenizer.perl
- Command line
-
hf download hf://Quagmire1/wiki-cased/tools/mosesdecoder/scripts/tokenizer/detokenizer.perl
-
curl -L -o detokenizer.perl https://huggingface.co/Quagmire1/wiki-cased/resolve/main/tools/mosesdecoder/scripts/tokenizer/detokenizer.perl
12.5 kB
| #!/usr/bin/env perl | |
| # $Id: detokenizer.perl 4134 2011-08-08 15:30:54Z bgottesman $ | |
| # Sample De-Tokenizer | |
| # written by Josh Schroeder, based on code by Philipp Koehn | |
| # further modifications by Ondrej Bojar | |
| # | |
| # This file is part of moses. Its use is licensed under the GNU Lesser General | |
| # Public License version 2.1 or, at your option, any later version. | |
| binmode(STDIN, ":utf8"); | |
| binmode(STDOUT, ":utf8"); | |
| use warnings; | |
| use strict; | |
| use utf8; # tell perl this script file is in UTF-8 (see all funny punct below) | |
| my $language = "en"; | |
| my $QUIET = 0; | |
| my $HELP = 0; | |
| my $UPPERCASE_SENT = 0; | |
| my $PENN = 0; | |
| while (@ARGV) { | |
| $_ = shift; | |
| /^-b$/ && ($| = 1, next); | |
| /^-l$/ && ($language = shift, next); | |
| /^-q$/ && ($QUIET = 1, next); | |
| /^-h$/ && ($HELP = 1, next); | |
| /^-u$/ && ($UPPERCASE_SENT = 1, next); | |
| /^-penn$/ && ($PENN = 1, next); | |
| } | |
| if ($HELP) { | |
| print "Usage ./detokenizer.perl (-l [en|fr|it|cs|...]) < tokenizedfile > detokenizedfile\n"; | |
| print "Options:\n"; | |
| print " -u ... uppercase the first char in the final sentence.\n"; | |
| print " -q ... don't report detokenizer revision.\n"; | |
| print " -b ... disable Perl buffering.\n"; | |
| print " -penn ... assume input is tokenized as per tokenizer.perl's -penn option.\n"; | |
| exit; | |
| } | |
| if ($language !~ /^(cs|en|fr|it|fi)$/) { | |
| print STDERR "Warning: No built-in rules for language $language.\n" | |
| } | |
| if ($PENN && $language ne "en") { | |
| print STDERR "Error: -penn option only supported for English text.\n"; | |
| exit; | |
| } | |
| if (!$QUIET) { | |
| print STDERR "Detokenizer Version ".'$Revision: 4134 $'."\n"; | |
| print STDERR "Language: $language\n"; | |
| } | |
| while(<STDIN>) { | |
| if (/^<.+>$/ || /^\s*$/) { | |
| #don't try to detokenize XML/HTML tag lines | |
| print $_; | |
| } elsif ($PENN) { | |
| print &detokenize_penn($_); | |
| } else { | |
| print &detokenize($_); | |
| } | |
| } | |
| sub ucsecondarg { | |
| # uppercase the second argument | |
| my $arg1 = shift; | |
| my $arg2 = shift; | |
| return $arg1.uc($arg2); | |
| } | |
| sub deescape { | |
| # de-escape special chars | |
| my ($text) = @_; | |
| $text =~ s/\&bar;/\|/g; # factor separator (legacy) | |
| $text =~ s/\|/\|/g; # factor separator | |
| $text =~ s/\</\</g; # xml | |
| $text =~ s/\>/\>/g; # xml | |
| $text =~ s/\&bra;/\[/g; # syntax non-terminal (legacy) | |
| $text =~ s/\&ket;/\]/g; # syntax non-terminal (legacy) | |
| $text =~ s/\"/\"/g; # xml | |
| $text =~ s/\'/\'/g; # xml | |
| $text =~ s/\[/\[/g; # syntax non-terminal | |
| $text =~ s/\]/\]/g; # syntax non-terminal | |
| $text =~ s/\&/\&/g; # escape escape | |
| return $text; | |
| } | |
| sub detokenize { | |
| my($text) = @_; | |
| chomp($text); | |
| $text = " $text "; | |
| $text =~ s/ \@\-\@ /-/g; | |
| $text = &deescape($text); | |
| my $word; | |
| my $i; | |
| my @words = split(/ /,$text); | |
| $text = ""; | |
| my %quoteCount = ("\'"=>0,"\""=>0); | |
| my $prependSpace = " "; | |
| for ($i=0;$i<(scalar(@words));$i++) { | |
| if (&startsWithCJKChar($words[$i])) { | |
| if (($i > 0 && &endsWithCJKChar($words[$i-1])) && ($language ne "ko")) { | |
| # perform left shift if this is a second consecutive CJK (Chinese/Japanese/Korean) word | |
| $text=$text.$words[$i]; | |
| } else { | |
| # ... but do nothing special if this is a CJK word that doesn't follow a CJK word | |
| $text=$text.$prependSpace.$words[$i]; | |
| } | |
| $prependSpace = " "; | |
| } elsif ($words[$i] =~ /^[\p{IsSc}\(\[\{\¿\¡]+$/) { | |
| #perform right shift on currency and other random punctuation items | |
| $text = $text.$prependSpace.$words[$i]; | |
| $prependSpace = ""; | |
| } elsif ($words[$i] =~ /^[\,\.\?\!\:\;\\\%\}\]\)]+$/){ | |
| if (($language eq "fr") && ($words[$i] =~ /^[\?\!\:\;\\\%]$/)) { | |
| #these punctuations are prefixed with a non-breakable space in french | |
| $text .= " "; } | |
| #perform left shift on punctuation items | |
| $text=$text.$words[$i]; | |
| $prependSpace = " "; | |
| } elsif (($language eq "en") && ($i>0) && ($words[$i] =~ /^[\'][\p{IsAlpha}]/) && ($words[$i-1] =~ /[\p{IsAlnum}]$/)) { | |
| #left-shift the contraction for English | |
| $text=$text.$words[$i]; | |
| $prependSpace = " "; | |
| } elsif (($language eq "cs") && ($i>1) && ($words[$i-2] =~ /^[0-9]+$/) && ($words[$i-1] =~ /^[.,]$/) && ($words[$i] =~ /^[0-9]+$/)) { | |
| #left-shift floats in Czech | |
| $text=$text.$words[$i]; | |
| $prependSpace = " "; | |
| } elsif ((($language eq "fr") ||($language eq "it")) && ($i<=(scalar(@words)-2)) && ($words[$i] =~ /[\p{IsAlpha}][\']$/) && ($words[$i+1] =~ /^[\p{IsAlpha}]/)) { | |
| #right-shift the contraction for French and Italian | |
| $text = $text.$prependSpace.$words[$i]; | |
| $prependSpace = ""; | |
| } elsif (($language eq "cs") && ($i<(scalar(@words)-3)) | |
| && ($words[$i] =~ /[\p{IsAlpha}]$/) | |
| && ($words[$i+1] =~ /^[-–]$/) | |
| && ($words[$i+2] =~ /^li$|^mail.*/i) | |
| ) { | |
| #right-shift "-li" in Czech and a few Czech dashed words (e-mail) | |
| $text = $text.$prependSpace.$words[$i].$words[$i+1]; | |
| $i++; # advance over the dash | |
| $prependSpace = ""; | |
| } elsif ($words[$i] =~ /^[\'\"„“`]+$/) { | |
| #combine punctuation smartly | |
| my $normalized_quo = $words[$i]; | |
| $normalized_quo = '"' if $words[$i] =~ /^[„“”]+$/; | |
| $quoteCount{$normalized_quo} = 0 | |
| if !defined $quoteCount{$normalized_quo}; | |
| if ($language eq "cs" && $words[$i] eq "„") { | |
| # this is always the starting quote in Czech | |
| $quoteCount{$normalized_quo} = 0; | |
| } | |
| if ($language eq "cs" && $words[$i] eq "“") { | |
| # this is usually the ending quote in Czech | |
| $quoteCount{$normalized_quo} = 1; | |
| } | |
| if (($quoteCount{$normalized_quo} % 2) eq 0) { | |
| if(($language eq "en") && ($words[$i] eq "'") && ($i > 0) && ($words[$i-1] =~ /[s]$/)) { | |
| #single quote for posesssives ending in s... "The Jones' house" | |
| #left shift | |
| $text=$text.$words[$i]; | |
| $prependSpace = " "; | |
| } else { | |
| #right shift | |
| $text = $text.$prependSpace.$words[$i]; | |
| $prependSpace = ""; | |
| $quoteCount{$normalized_quo} ++; | |
| } | |
| } else { | |
| #left shift | |
| $text=$text.$words[$i]; | |
| $prependSpace = " "; | |
| $quoteCount{$normalized_quo} ++; | |
| } | |
| } elsif (($language eq "fi") && ($words[$i-1] =~ /:$/) && ($words[$i] =~ /^(N|n|A|a|Ä|ä|ssa|Ssa|ssä|Ssä|sta|stä|Sta|Stä|hun|Hun|hyn|Hyn|han|Han|hän|Hän|hön|Hön|un|Un|yn|Yn|an|An|än|Än|ön|Ön|seen|Seen|lla|Lla|llä|Llä|lta|Lta|ltä|Ltä|lle|Lle|ksi|Ksi|kse|Kse|tta|Tta|ine|Ine)(ni|si|mme|nne|nsa)?(ko|kö|han|hän|pa|pä|kaan|kään|kin)?$/)) { | |
| # Finnish : without intervening space if followed by case suffix | |
| # EU:N EU:n EU:ssa EU:sta EU:hun EU:iin ... | |
| $text=$text. lc $words[$i]; | |
| $prependSpace = " "; | |
| } else { | |
| $text=$text.$prependSpace.$words[$i]; | |
| $prependSpace = " "; | |
| } | |
| } | |
| # clean up spaces at head and tail of each line as well as any double-spacing | |
| $text =~ s/ +/ /g; | |
| $text =~ s/\n /\n/g; | |
| $text =~ s/ \n/\n/g; | |
| $text =~ s/^ //g; | |
| $text =~ s/ $//g; | |
| #add trailing break | |
| $text .= "\n" unless $text =~ /\n$/; | |
| $text =~ s/^([[:punct:]\s]*)([[:alpha:]])/ucsecondarg($1, $2)/e if $UPPERCASE_SENT; | |
| return $text; | |
| } | |
| sub detokenize_penn { | |
| my($text) = @_; | |
| chomp($text); | |
| $text = " $text "; | |
| $text =~ s/ \@\-\@ /-/g; | |
| $text =~ s/ \@\/\@ /\//g; | |
| $text = &deescape($text); | |
| # merge de-contracted forms except where the second word begins with an | |
| # apostrophe (those are handled later) | |
| $text =~ s/ n't /n't /g; | |
| $text =~ s/ N'T /N'T /g; | |
| $text =~ s/ ([Cc])an not / $1annot /g; | |
| $text =~ s/ ([Dd])' ye / $1'ye /g; | |
| $text =~ s/ ([Gg])im me / $1imme /g; | |
| $text =~ s/ ([Gg])on na / $1onna /g; | |
| $text =~ s/ ([Gg])ot ta / $1otta /g; | |
| $text =~ s/ ([Ll])em me / $1emme /g; | |
| $text =~ s/ '([Tt]) is / '$1is /g; | |
| $text =~ s/ '([Tt]) was / '$1was /g; | |
| $text =~ s/ ([Ww])an na / $1anna /g; | |
| # restore brackets | |
| $text =~ s/-LRB-/\(/g; | |
| $text =~ s/-RRB-/\)/g; | |
| $text =~ s/-LSB-/\[/g; | |
| $text =~ s/-RSB-/\]/g; | |
| $text =~ s/-LCB-/{/g; | |
| $text =~ s/-RCB-/}/g; | |
| my $i; | |
| my @words = split(/ /,$text); | |
| $text = ""; | |
| my $prependSpace = " "; | |
| for ($i=0;$i<(scalar(@words));$i++) { | |
| if ($words[$i] =~ /^[\p{IsSc}\(\[\{\¿\¡]+$/) { | |
| # perform right shift on currency and other random punctuation items | |
| $text = $text.$prependSpace.$words[$i]; | |
| $prependSpace = ""; | |
| } elsif ($words[$i] =~ /^[\,\.\?\!\:\;\\\%\}\]\)]+$/){ | |
| # perform left shift on punctuation items | |
| $text=$text.$words[$i]; | |
| $prependSpace = " "; | |
| } elsif (($i>0) && ($words[$i] =~ /^[\'][\p{IsAlpha}]/) && ($words[$i-1] =~ /[\p{IsAlnum}]$/)) { | |
| # left-shift the contraction | |
| $text=$text.$words[$i]; | |
| $prependSpace = " "; | |
| } elsif ($words[$i] eq "`") { # Assume that punctuation has been normalized and is one of `, ``, ', '' only | |
| # opening single quote: convert to straight quote and right-shift | |
| $text = $text.$prependSpace."\'"; | |
| $prependSpace = ""; | |
| } elsif ($words[$i] eq "``") { | |
| # opening double quote: convert to straight quote and right-shift | |
| $text = $text.$prependSpace."\""; | |
| $prependSpace = ""; | |
| } elsif ($words[$i] eq "\'") { | |
| # closing single quote: convert to straight quote and left shift | |
| $text = $text."\'"; | |
| $prependSpace = " "; | |
| } elsif ($words[$i] eq "\'\'") { | |
| # closing double quote: convert to straight quote and left shift | |
| $text = $text."\""; | |
| $prependSpace = " "; | |
| } else { | |
| $text = $text.$prependSpace.$words[$i]; | |
| $prependSpace = " "; | |
| } | |
| } | |
| # clean up spaces at head and tail of each line as well as any double-spacing | |
| $text =~ s/ +/ /g; | |
| $text =~ s/\n /\n/g; | |
| $text =~ s/ \n/\n/g; | |
| $text =~ s/^ //g; | |
| $text =~ s/ $//g; | |
| # add trailing break | |
| $text .= "\n" unless $text =~ /\n$/; | |
| $text =~ s/^([[:punct:]\s]*)([[:alpha:]])/ucsecondarg($1, $2)/e if $UPPERCASE_SENT; | |
| return $text; | |
| } | |
| sub startsWithCJKChar { | |
| my ($str) = @_; | |
| return 0 if length($str) == 0; | |
| my $firstChar = substr($str, 0, 1); | |
| return &charIsCJK($firstChar); | |
| } | |
| sub endsWithCJKChar { | |
| my ($str) = @_; | |
| return 0 if length($str) == 0; | |
| my $lastChar = substr($str, length($str)-1, 1); | |
| return &charIsCJK($lastChar); | |
| } | |
| # Given a string consisting of one character, returns true iff the character | |
| # is a CJK (Chinese/Japanese/Korean) character | |
| sub charIsCJK { | |
| my ($char) = @_; | |
| # $char should be a string of length 1 | |
| my $codepoint = &codepoint_dec($char); | |
| # The following is based on http://en.wikipedia.org/wiki/Basic_Multilingual_Plane#Basic_Multilingual_Plane | |
| # Hangul Jamo (1100–11FF) | |
| return 1 if (&between_hexes($codepoint, '1100', '11FF')); | |
| # CJK Radicals Supplement (2E80–2EFF) | |
| # Kangxi Radicals (2F00–2FDF) | |
| # Ideographic Description Characters (2FF0–2FFF) | |
| # CJK Symbols and Punctuation (3000–303F) | |
| # Hiragana (3040–309F) | |
| # Katakana (30A0–30FF) | |
| # Bopomofo (3100–312F) | |
| # Hangul Compatibility Jamo (3130–318F) | |
| # Kanbun (3190–319F) | |
| # Bopomofo Extended (31A0–31BF) | |
| # CJK Strokes (31C0–31EF) | |
| # Katakana Phonetic Extensions (31F0–31FF) | |
| # Enclosed CJK Letters and Months (3200–32FF) | |
| # CJK Compatibility (3300–33FF) | |
| # CJK Unified Ideographs Extension A (3400–4DBF) | |
| # Yijing Hexagram Symbols (4DC0–4DFF) | |
| # CJK Unified Ideographs (4E00–9FFF) | |
| # Yi Syllables (A000–A48F) | |
| # Yi Radicals (A490–A4CF) | |
| return 1 if (&between_hexes($codepoint, '2E80', 'A4CF')); | |
| # Phags-pa (A840–A87F) | |
| return 1 if (&between_hexes($codepoint, 'A840', 'A87F')); | |
| # Hangul Syllables (AC00–D7AF) | |
| return 1 if (&between_hexes($codepoint, 'AC00', 'D7AF')); | |
| # CJK Compatibility Ideographs (F900–FAFF) | |
| return 1 if (&between_hexes($codepoint, 'F900', 'FAFF')); | |
| # CJK Compatibility Forms (FE30–FE4F) | |
| return 1 if (&between_hexes($codepoint, 'FE30', 'FE4F')); | |
| # Range U+FF65–FFDC encodes halfwidth forms, of Katakana and Hangul characters | |
| return 1 if (&between_hexes($codepoint, 'FF65', 'FFDC')); | |
| # Supplementary Ideographic Plane 20000–2FFFF | |
| return 1 if (&between_hexes($codepoint, '20000', '2FFFF')); | |
| return 0; | |
| } | |
| # Returns the code point of a Unicode char, represented as a decimal number | |
| sub codepoint_dec { | |
| if (my $char = shift) { | |
| return unpack('U0U*', $char); | |
| } | |
| } | |
| sub between_hexes { | |
| my ($num, $left, $right) = @_; | |
| return $num >= hex($left) && $num <= hex($right); | |
| } | |