javascript - str.replace(/^/gm, "\t") ifadesinin beklenen davranışı nedir


Her satırın önüne bir \t eklemesi için str.replace(/^/gm, "\t") beklerdim. Görünüşe göre durum böyle, ama sadece LF sonları kullanıldığında, CRLF sonları kullanıldığında ise garip bir şey oluyor, bunu \n arasına da ekliyor.

Bu örnek kodu göz önünde bulundurun:

var str1 = "First line\nnext Line";
var str2 = "First line\r\nnext Line";

function escape(str) {
  return str.replace(/[\r\n\t]/g, match => {
    return {
      '\r': '\\r',
      '\n': '\\n',
      '\t': '\\t',
    }[match]
  })
}

console.log("First string:")
console.log(escape(str1))


console.log("Second string:")
console.log(escape(str2))

function operation(str) {
  return str.replace(/^/gm, "\t");
}

console.log("First string:")
str1 = operation(str1)
console.log(escape(str1))


console.log("Second string:")
str2 = operation(str2)
console.log(escape(str2))
.as-console-wrapper {
    max-height: 100% !important;
}

Dönüşümden sonra, ilk dize beklenen şekilde \tFirst line\n\tnext Line olurken, ikinci dize \tFirst line\r\t\n\tnext Line sonucunu veriyor ve bu, kullandığım bir VSCode uzantısında beklenmeyen davranışlara yol açıyor. VSCode her \n ve \r karakterini ayrı ayrı yorumlayıp her birini \n\r ile değiştiriyor, bu da istenmeyen biçimlendirmeye neden oluyor. Konsol ise bunu önemsemiyor ve beklenen şekilde gösteriyor; bu yüzden o kötü dönüşümlü satır sonlarını ve satır başlarını göstermek için escape fonksiyonu kullanılıyor.

Bu beklenen bir davranış mı yoksa JavaScript standart kütüphanesinde bir hata mı?

Cevap #1

Bu tam olarak ECMAScript spesifikasyonuna uygundur, 22.2.2.4 Çalışma Zamanı Anlamları: CompileAssertion:

Assertion :: ^

  1. Return a new Matcher with parameters (x, c) that captures nothing and performs
     the following steps when called:
      a. Assert: x is a State.
      b. Assert: c is a Continuation.
      c. Let e be x's endIndex.
      d. If e = 0, or if Multiline is true and the character Input[e - 1] is one of
         LineTerminator, then
          i. Return c(x).
      e. Return failure.

Başka bir yerde, Bölüm 12.3'te LineTerminator <LF> <CR> <LS> <PS> öğelerinden biri olarak tanımlanmıştır, bu nedenle ve \n gerçekten de ayrı sonlandırıcılar olarak görülür.

Muhtemelen en kolay çözüm, m bayrağını kullanmamak ve tüm satır sonu türlerini kendiniz eşleştirmektir (geçmişte Mac'te kullanılan \r dahil):

>>> "foo\rbar\r\nbaz\nqux".replace(/(^|\r\n|\r|\n)/g, "$1\t")
"\tfoo\r\tbar\r\n\tbaz\n\tqux"

Dikkat edin, \r\n, \r'den önce gelmelidir, böylece tek bir birim olarak eşleşir.

İlgili makaleler