【问题标题】:Why does Node.js allow this seemingly invalid character sequence?为什么 Node.js 允许这种看似无效的字符序列?
【发布时间】:2020-04-22 08:16:58
【问题描述】:

我正在寻找是否有办法区分文件中的返回(下一行)和键入的换行符(文件中的\n)。当我在REPL 中玩耍时,我在比较中打错了字,令我惊讶的是,Node.js 并不在意。它甚至给出了我认为未定义的行为,除非我在多年的 Node.js 亲密关系中完全错过了一些东西。而且我在玩的过程中还发现了一些其他的东西,我会问下面的。

代码在帖子底部。

主要问题是:

为什么 Node.js 在最后两次比较(==+ 和 ==-)时没有抱怨语法?那是某种有效的语法吗?为什么在没有尾随 +/- 的情况下比较为真?它是假的? (帖子中的更新)

主要的问题是:

为什么当所有其他测试都为真时,“缓冲区单独自我比较”和“缓冲区比较”结果却为假?为什么缓冲区不能与相同数据的缓冲区进行比较?

还有:

如上所述,我如何可靠地区分文件中的返回和键入的换行符?

代码如下:


const nl = '\n'
const newline = `
`

const NL = Buffer.from('\n')
const NEWLINE = Buffer.from(`
`)
const NEWLINE2 = Buffer.from(`
`)
console.log("Buffer separate self comparison: "+(NEWLINE2 == NEWLINE))
console.log("Buffer comparison: "+(NL == NEWLINE))
console.log("Non buffer comparison: "+(nl == newline))
console.log("Buffer self comparison 1: "+(NL == NL))
console.log("Buffer self comparison 2: "+(NEWLINE == NEWLINE))
console.log("Buffer/String comparison 1: "+(nl == NL))
console.log("Buffer/String comparison 2: "+(newline == NEWLINE))
console.log("Buffer/String cross comparison 1: "+(nl == NEWLINE))
console.log("Buffer/String cross comparison 2: "+(newline == NL))
console.log("Buffer toString comparison: "+(NL.toString() == NEWLINE.toString()))
console.log("Strange operator comparison 1: "+(NL ==+ NEWLINE))
console.log("Strange operator comparison 2: "+(NL ==- NEWLINE))

【问题讨论】:

  • NL ==+ NEWLINE 被评估为NL == (+NEWLINE)
  • 嗯,好的。所以我玩弄了它,并且 (+NEWLINE) (和所有其他字符串)评估为 NaN,它仍然不应该等于字符串
  • NEWLINE 不是字符串而是缓冲区。 +NEWLINE 计算结果为 0
  • 好的,我再试一次。 (+Buffer) 计算结果为 NaN,除非 Buffer 是转义序列,例如 tab|carriage|return。为什么是现在? (+Buffer) 应该是 NaN(什么是正缓冲区?),但为什么转义序列会导致其评估为 0?
  • 您可能需要查看 Node.js 的源代码才能了解为什么将 Buffer 强制转换为数字可能会返回 0。据我所知,文档中没有关于该行为的任何内容。 0 == '\n' 的计算结果为 true,因为这就是 Abstract Equality Comparison 指定它的方式。如果等号的任一侧是数字而另一侧是字符串,则字符串将被强制转换为数字。 Number('\n') 的计算结果为 0

标签: node.js undefined-behavior comparison-operators


【解决方案1】:
NEWLINE2 == NEWLINE (false)
NL == NEWLINE (false)

仅当操作数引用相同的对象时,比较对象的表达式才为真。 src

事实并非如此:它们是两个独立的对象,即使它们的初始值相同,所以结果是false

编辑:如果你想比较两个 Buffer 的 values 而不是 identity,你可以使用Buffer.compareBuffer.compare(NEWLINE2, NEWLINE) === 0 表示两者相等。

nl == newline (true)

当两个字符串具有相同的字符序列、相同的长度和对应位置的相同字符时,它们是严格相等的。 src

字符串相等,所以true

NL == NL (true)
NEWLINE == NEWLINE (true)

仅当操作数引用相同的对象时,比较对象的表达式才为真。 src

nl == NL (true)
newline == NEWLINE (true)
nl == NEWLINE (true)
newline == NL (true)

这里发生的情况是您在比较两种不同的类型。一个是字符串,另一个是对象。

在进行比较之前,这些运算符中的每一个都会将其操作数强制转换为原语。如果两者都以字符串结尾,则使用字典顺序进行比较,否则将它们转换为要比较的数字。与 NaN 的比较总是会产生错误。 src

Buffer 有一个 toString 方法,因此调用它是为了在 == 的两侧具有相同的原始类型。此方法的结果是一个包含\n 的字符串。 '\n' == '\n'true

顺便说一句,如果您的比较是NEWLINE == 0,那么会发生这种情况:

' 1 ' == 1 等于真。转换时,空格被丢弃,因此' 1 ' 将转换为值为 1 的数字。结果比较将是1 == 1

只有空白字符的字符串将被强制转换为0。缓冲区首先转换为字符串,然后转换为整数,因此会发生这种情况:0 == 0,因此结果将是true

NL.toString() == NEWLINE.toString() (true)

当两个字符串具有相同的字符序列、相同的长度和对应位置的相同字符时,它们是严格相等的。 src

字符串相等,所以true

NL ==+ NEWLINE (true)
NL ==- NEWLINE (true)

这与== +NEWLINE 相同。您正在使用一元 +- 显式转换为数字。有趣的是,您正在做这些比较,在投射之后:0 == +00 == -0。负零和正零are considered equal

这里没有一个行为是“未定义的”。

除了“嗯,这很好”之外,真的几乎没有理由不使用严格相等运算符 (===),它不会将事物转换为相同的原语。


至于你的问题:

文件中的换行符 (\n) 与自键入字符串 ('\n') 中的换行符相同。它们都是 ASCII 或 Unicode 字符 0x0A,按字节计算。

有些文档同时包含换行符和回车符。换行符由两个字符组成:0x0D 0x0A(或\r\n)。

【讨论】:

  • 非常感谢善良的人。我认为隔离让我吸了太多太空药草,我完全想太多了。我被要求在 nodejs(纯 js)中编写编译器/解释器,我希望有一种方法来区分行尾和源代码中遇到显式 \n 时的区别,这让我在这个兔子洞里
  • 说到这一点,我正在考虑做一个崇高的扩展,让一些随机的未充分使用的 ascii 值终止这些行,并在源代码中查找这些行。你认为这行得通吗?假设 sublime text 可以让我像这样更改行终止符而不会输出垃圾。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2011-04-26
  • 1970-01-01
  • 2012-10-27
相关资源
最近更新 更多