prefilter/neologd.go
package prefilter
import (
"bytes"
"strings"
"unicode"
"unicode/utf8"
"github.com/evalphobia/go-jp-text-ripper/ripper"
)
// Neologd is prefilter to normalize text by neologd recommended format
var Neologd = &ripper.PreFilter{
Fn: func(rawText string) string {
return NormalizeNeologd(rawText)
},
}
// Most of code logic is from https://github.com/ikawaha/x/neologd/neologd.go
const (
prolongedSoundMark = '\u30FC'
)
var latinSymbols = &unicode.RangeTable{
R16: []unicode.Range16{
{0x0021, 0x0040, 1},
{0x005B, 0x0060, 1},
{0x007B, 0x007E, 1},
},
}
var neologdReplacer = strings.NewReplacer(
"0", "0", "1", "1", "2", "2", "3", "3", "4", "4",
"5", "5", "6", "6", "7", "7", "8", "8", "9", "9",
"A", "A", "B", "B", "C", "C", "D", "D", "E", "E",
"F", "F", "G", "G", "H", "H", "I", "I", "J", "J",
"K", "K", "L", "L", "M", "M", "N", "N", "O", "O",
"P", "P", "Q", "Q", "R", "R", "S", "S", "T", "T",
"U", "U", "V", "V", "W", "W", "X", "X", "Y", "Y",
"Z", "Z",
"a", "a", "b", "b", "c", "c", "d", "d", "e", "e",
"f", "f", "g", "g", "h", "h", "i", "i", "j", "j",
"k", "k", "l", "l", "m", "m", "n", "n", "o", "o",
"p", "p", "q", "q", "r", "r", "s", "s", "t", "t",
"u", "u", "v", "v", "w", "w", "x", "x", "y", "y",
"z", "z",
//small case
"ァ", "ァ", "ィ", "ィ", "ゥ", "ゥ", "ェ", "ェ", "ォ", "ォ",
"ャ", "ャ", "ュ", "ュ", "ョ", "ョ", "ッ", "ッ",
"ア", "ア", "イ", "イ", "ウ", "ウ", "エ", "エ", "オ", "オ",
"ガ", "ガ", "ギ", "ギ", "グ", "グ", "ゲ", "ゲ", "ゴ", "ゴ",
"カ", "カ", "キ", "キ", "ク", "ク", "ケ", "ケ", "コ", "コ",
"ザ", "ザ", "ジ", "ジ", "ズ", "ズ", "ゼ", "ゼ", "ゾ", "ゾ",
"サ", "サ", "シ", "シ", "ス", "ス", "セ", "セ", "ソ", "ソ",
"ダ", "ダ", "ヂ", "ヂ", "ヅ", "ヅ", "デ", "デ", "ド", "ド",
"タ", "タ", "チ", "チ", "ツ", "ツ", "テ", "テ", "ト", "ト",
"ナ", "ナ", "ニ", "ニ", "ヌ", "ヌ", "ネ", "ネ", "ノ", "ノ",
"バ", "バ", "ビ", "ビ", "ブ", "ブ", "ベ", "ベ", "ボ", "ボ",
"パ", "パ", "ピ", "ピ", "プ", "プ", "ペ", "ペ", "ポ", "ポ",
"ハ", "ハ", "ヒ", "ヒ", "フ", "フ", "ヘ", "ヘ", "ホ", "ホ",
"マ", "マ", "ミ", "ミ", "ム", "ム", "メ", "メ", "モ", "モ",
"ヤ", "ヤ", "ユ", "ユ", "ヨ", "ヨ",
"ラ", "ラ", "リ", "リ", "ル", "ル", "レ", "レ", "ロ", "ロ",
"ワ", "ワ", "ヲ", "ヲ", "ン", "ン",
// hyphen
"\u02D7", "-", "\u058A", "-", "\u2010", "-", "\u2011", "-", "\u2012", "-",
"\u2013", "-", "\u2043", "-", "\u207B", "-", "\u208B", "-", "\u2212", "-",
// bar
"\u2014", string(prolongedSoundMark), // エムダッシュ
"\u2015", string(prolongedSoundMark), // ホリゾンタルバー
"\u2500", string(prolongedSoundMark), // 横細罫線
"\u2501", string(prolongedSoundMark), // 横太罫線
"\uFE63", string(prolongedSoundMark), // SMALL HYPHEN-MINUS
"\uFF0D", string(prolongedSoundMark), // 全角ハイフンマイナス
"\uFF70", string(prolongedSoundMark), // 半角長音記号
// tilde
"~", "", "\u223C", "", "\u223E", "", "\u301C", "", "\u3030", "", "\uFF5E", "",
// zen -> han
"!", "!", "”", `"`, "#", "#", "$", "$", "%", "%",
"&", "&", `’`, `'`, "(", "(", ")", ")", "*", "*",
"+", "+", ",", ",", "−", "-", ".", ".", "/", "/",
":", ":", ";", ";", "<", "<", ">", ">", "?", "?",
"@", "@", "[", "[", "¥", "\u00A5", "]", "]", "^", "^",
"_", "_", "`", "`", "{", "{", "|", "|", "}", "}",
" ", " ",
// han -> zen
"。", "。", "、", "、", "・", "・", "=", "=", "「", "「", "」", "」",
"\n", " ", `\n`, " ", "\t", " ", `\t`, " ", "\v", " ", `\v`, " ",
)
// NormalizeNeologd normalizes text
func NormalizeNeologd(s string) string {
s = neologdReplacer.Replace(s)
s = eliminateSpace(s)
s = shurinkProlongedSoundMark(s)
return s
}
func eliminateSpace(s string) string {
var (
b bytes.Buffer
prev rune
)
for p := 0; p < len(s); {
c, w := utf8.DecodeRuneInString(s[p:])
p += w
if !unicode.IsSpace(c) {
_, err := b.WriteRune(c)
if err == nil {
prev = c
}
continue
}
for p < len(s) {
c0, w0 := utf8.DecodeRuneInString(s[p:])
p += w0
if !unicode.IsSpace(c0) {
if unicode.In(prev, unicode.Latin, latinSymbols) &&
unicode.In(c0, unicode.Latin, latinSymbols) {
_, _ = b.WriteRune(' ')
}
_, _ = b.WriteRune(c0)
prev = c0
break
}
}
}
return b.String()
}
func shurinkProlongedSoundMark(s string) string {
var b bytes.Buffer
for p := 0; p < len(s); {
c, w := utf8.DecodeRuneInString(s[p:])
p += w
_, err := b.WriteRune(c)
if err != nil || c != prolongedSoundMark {
continue
}
for p < len(s) {
c0, w0 := utf8.DecodeRuneInString(s[p:])
p += w0
if c0 != prolongedSoundMark {
_, _ = b.WriteRune(c0)
break
}
}
}
return b.String()
}