Golang заменит все символы новой строки - PullRequest
0 голосов
/ 01 октября 2018

Обычно, когда я заменяю символы новой строки, я перехожу на Regexp, как в этом PHP

preg_replace('/\R/u', "\n", $String);

Потому что я знаю, что это очень надежный способ заменить любой тип новой строки Unicode (будь то \n, \ r, \ r \ n и т. д.)

Я тоже пытался что-то подобное в Go, но я получаю

ошибка анализа regexp: недопустимая escape-последовательность: \R

В этой строке

msg = regexp.MustCompilePOSIX("\\R").ReplaceAllString(html.EscapeString(msg), "<br>\n")

Я пытался использовать (?:(?>\r\n)|\v) из https://stackoverflow.com/a/4389171/728236,, но похоже, что реализация в регулярном выражении Go не поддерживает этолибо, паникуя с помощью invalid or unsupported Perl syntax: '(?>'

Какой хороший, безопасный способ заменить символы новой строки в Go, Regex или нет?


Я вижу этот ответ здесь Golang: проблемы с заменойпереводы строк в текстовом файле , говорящие об использовании \r?\n, но я не решаюсь поверить, что он получит все переводы Unicode, в основном из-за этого вопроса, в котором есть ответ на многие другие вопросыновые строки кода, отличные от 3, которые \r?\n охватывают,

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Хотя регулярное выражение regexp обычно дает элегантное и компактное решение, зачастую оно не самое быстрое.

Для задач, в которых необходимо заменить одни подстроки другими, стандартная библиотека предоставляет действительно эффективное решение в видеstrings.Replacer:

Replacer заменяет список строк заменой.Это безопасно для одновременного использования несколькими программами.

Вы можете создать заменитель многократного использования с strings.NewReplacer(), где вы перечисляете пары, содержащие заменяемые части и их замены.Если вы хотите выполнить замену, просто позвоните Replacer.Replace().

Вот как это будет выглядеть:

const replacement = "<br>\n"

var replacer = strings.NewReplacer(
    "\r\n", replacement,
    "\r", replacement,
    "\n", replacement,
    "\v", replacement,
    "\f", replacement,
    "\u0085", replacement,
    "\u2028", replacement,
    "\u2029", replacement,
)

func replaceReplacer(s string) string {
    return replacer.Replace(s)
}

Вот как решение регулярного выражения из ответ Wiktor выглядит так:

var re = regexp.MustCompile(`\r\n|[\r\n\v\f\x{0085}\x{2028}\x{2029}]`)

func replaceRegexp(s string) string {
    return re.ReplaceAllString(s, "<br>\n")
}

Реализация на самом деле довольно быстрая.Вот простой тест, сравнивающий его с вышеупомянутым предварительно скомпилированным решением регулярного выражения:

const input = "1st\nsecond\r\nthird\r4th\u0085fifth\u2028sixth"

func BenchmarkReplacer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        replaceReplacer(input)
    }
}

func BenchmarkRegexp(b *testing.B) {
    for i := 0; i < b.N; i++ {
        replaceRegexp(input)
    }
}

И результаты теста:

BenchmarkReplacer-4      3000000               495 ns/op
BenchmarkRegexp-4         500000              2787 ns/op

Для нашего тестового ввода strings.Replacer было больше, чем *В 1035 * 5 раз быстрее.

Есть и другое преимущество.В приведенном выше примере мы получаем результат в виде нового значения string (в обоих решениях).Это требует нового string распределения.Если нам нужно записать результат в io.Writer (например, мы создаем ответ HTTP или записываем результат в файл), мы можем избежать создания нового string в случаеstrings.Replacer, поскольку у него есть удобный метод Replacer.WriteString(), который принимает io.Writer и записывает результат в него, не выделяя и не возвращая его как string.Это значительно увеличивает прирост производительности по сравнению с решением регулярных выражений.

0 голосов
/ 01 октября 2018

Вы можете «декодировать» шаблон \R как

U+000DU+000A|[U+000AU+000BU+000CU+000DU+0085U+2028U+2029]

См. Документы Java regex , поясняющие сокращение \R:

<b>Linebreak matcher</b>
\R  Any Unicode linebreak sequence, is equivalent to \u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

В Go вы можете использовать следующее:

func removeLBR(text string) string {
    re := regexp.MustCompile(`\x{000D}\x{000A}|[\x{000A}\x{000B}\x{000C}\x{000D}\x{0085}\x{2028}\x{2029}]`)
    return re.ReplaceAllString(text, ``)
}

Вот демо Go .

Некоторые из кодов Unicode могут быть заменены escape-последовательностями регулярных выражений, поддерживаемыми Go regexp :

re := regexp.MustCompile(`\r\n|[\r\n\v\f\x{0085}\x{2028}\x{2029}]`)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...