该函数的目的是解析 CSV 文件的头部行(即第一行),并提取出所有的列名。CSV 文件是一种常用的文本格式,用于存储结构化数据,其中列与列之间通常用逗号分隔。有时,列名或列值中可能包含逗号,这时需要用双引号将整个列名或列值括起来,以确保正确的解析。
工作原理
正则表达式:
/"(?:[^"]|"")*"|,|\r\n|\n|\r/gm
:
"
:匹配双引号,表示一个可能包含逗号的列名的开始或结束。(?:[^"]|"")*
:匹配零个或多个非双引号字符,或者一个双引号字符(如果前一个字符也是双引号,则表示转义的双引号)。"
:匹配双引号,表示一个可能包含逗号的列名的结束。|
:逻辑 “或” 操作符,用于匹配逗号、换行符等。,
:匹配逗号,表示列与列之间的分隔符。\r\n|\n|\r
:匹配行结束符,即回车符、换行符或回车换行符的组合。gm
:g
表示全局匹配,m
表示多行匹配。解析逻辑:
- 初始化一个空数组
matches
来存储解析出的列名。- 初始化一个变量
startIndex
来记录上一个匹配的起始位置。- 使用
while
循环和regex.exec(headerLine)
来逐个匹配列名或分隔符。- 如果当前匹配的起始位置
match.index
大于startIndex
,则表示我们找到了一个新的列名(或列名的一部分)。将这部分内容添加到matches
数组中。- 更新
startIndex
为当前匹配的结束位置,即match.index + match[0].length
。- 循环结束后,检查是否还有剩余的文本未被匹配。如果有,将其作为一个额外的列名添加到
matches
数组中(这种情况可能发生在列名包含换行符时)。- 最后,返回
matches
数组,其中包含了所有的列名。例子
假设
headerLine
是a,"b,c",d
。
- 正则表达式首先匹配到
a
,然后是"
,接着是"b,c"
(其中包含逗号),然后是"
,最后是,
。- 在第一次循环中,
startIndex
是 0,match.index
是 1,所以match.index > startIndex
成立。因此,"a"
被添加到matches
数组中。startIndex
更新为 2(即match.index + match[0].length
)。- 下一次循环匹配到
"
,"b,c"
和"
,但match.index
(此时是 5)不大于startIndex
(此时是 2),所以这部分不被添加到matches
数组中。- 循环继续,匹配到
,
,然后是d
。- 最后,因为
startIndex
(此时是 9)小于headerLine.length
(此时是 10),所以"d"
被添加到matches
数组中。- 函数返回
matches
数组,此时包含["a", "b,c", "d"]
。这样,即使列名中包含逗号或换行符,函数也能正确地将它们解析为单独的列名
代码如下(可以直接调用):
const handleFileChange = (file) => {if (file && file.raw) {const reader = new FileReader();reader.onload = (e) => {const csvData = e.target.result;const lines = csvData.split('\n');if (lines.length > 0) {const header = parseHeader(lines[0]);columns.value = header; // 读取所有列名fileSelected.value = true;}};uploadFile1(file.raw);reader.readAsText(file.raw);}
};
const parseHeader = (headerLine) => {const regex = /"(?:[^"]|"")*"|,|\r\n|\n|\r/gm;let match;const matches = [];let startIndex = 0; // 记录上一个匹配的起始位置while ((match = regex.exec(headerLine)) !== null) {// 检查是否是列名的一部分if (match.index > startIndex) {// 添加之前匹配的列名matches.push(headerLine.substring(startIndex, match.index).replace(/""/g, '"'));}startIndex = match.index + match[0].length; // 更新下一个匹配的起始位置}// 添加最后一列(如果有的话)if (startIndex < headerLine.length) {matches.push(headerLine.substring(startIndex).replace(/""/g, '"'));}return matches;
};