我有一个两列的电子表格(以CSV格式保存),如下所示:
COLUMN 1,COLUMN 2
3-Entrepreneurship,"innovation, daily"
,countless
2-Police/Enforcement,"innocent, protect"
2-Bathroom:home room,toilet handle
3-Companies,née dresses
2-Sense of Smell,odorless
3-Entrepreneurship,old ideas
3-Entrepreneurship,¡new income streams!
3-Companies,Zoë’s food store
,many
2-Police/Enforcement,crime
2-Bathroom:home room,bath room
,ring
3-Companies,móvíl résumés
2-Sense of Smell,musty smell
3-Entrepreneurship,good publicity guru!
3-Companies,Señor
字符串
x1c 0d1x的数据
完整的电子表格有1000行(以CSV格式保存,逗号用于分隔两列)。它包含的类别多于此处列出的第1列。
如图所示,第2列的一些条目由两个或三个单词组成,中间用空格隔开。它们还使用逗号、撇号和重音字符(这些字符出现在多个类别中,而不仅仅是标题为3-Companies的类别)。
我想把CSV文件按照第1列中的值拆分成单独的TXT文件。单独的文件将不再是电子表格,而只是一个单词列表。
例如,拆分后
- 在文件3-创业.txt*
3-Entrepreneurship
innovation, daily
old ideas
¡new income streams!
good publicity guru!
型
- 在文件2-浴室:家庭房间.txt*
2-Bathroom:home room
toilet handle
bath room
型
- 在文件2-警察/执法.txt*
2-Police/Enforcement
innocent, protect
crime
型
- 在文件2中-Sense of sweet.txt *
2-Sense of Smell
odorless
musty smell
型
- 在文件3-Companies.txt中 *
3-Companies
née dresses
Zoë’s food store
móvíl résumés
Señor
型
这只是一个示例。完整的文件有超过5个类别(在第2列中),因此将有超过5个拆分后的文件。
环境我在MacOS 12.6.9中使用终端。理想情况下,我希望复制并粘贴一行代码,并使其作用于终端活动目录中的CSV文件(因此我不必将文件名硬编码到代码中)。
初次尝试
我实际上问了这个问题的一个不同的变体here。在那个版本中,第2列(而不是第1列)被用来进行分割。那个版本也没有把类别作为每个分割的TXT文件的第一行。
我试着修改它,如下所示:
tail -n +2 *.csv | sort -t',' -k2 | awk -F',' '$2~/^[[:space:]]*$/{next} {sub(/\x0d$/,"")} $2!=prev{close(out); out=$2".txt"; prev=$2} {print $1 > out}'
型
但是,尽管它按列1拆分并将类别名称放在顶部,但它忽略了类别2的所有内容,而是将所有类别2值拆分为单独的文件。
请注意:我不受此代码的约束。任何适用于MacOS 12.6.9的解决方案都可以。
7条答案
按热度按时间oewdyzsn1#
一个
awk
想法:字符串
**注意:**此解决方案将替换OP当前的所有
tail | sort | awk
代码这将产生:
型
nkhmeac62#
使用任何POSIX awk,不管会生成多少输出文件,并且不将所有输入存储在内存中,它都会正确处理输入中的转义双引号(例如,它会将
a,"foo""bar",b
转换为a,foo"bar,b
而不是a,foo""bar,b
):字符串
型
型
或者,你可以使用Decorate-Sort-Undecorate idiom来提高效率,因为它不需要在每次写入时打开/关闭输出文件,每个标签只需要打开/关闭一次:
型
你可以称之为
./tst.sh file.csv
。DSU脚本与将所有输入存储在awk中然后在
END
部分中处理它相比的一个好处是,在上面的情况下,只有sort
需要一次操作整个输入,而不是awk
,并且sort
使用请求分页等来处理任意大的输入文件。gj3fmq9x3#
这里有一个Ruby来做这件事:
字符串
(Note,在示例输入中将文件名中的字符
/
替换为:
,以生成2-Police:Enforcement.txt
,因为2-Police/Enforcement.txt
是非法文件名。)制作:
型
cbwuti444#
你也可以用Perl来解决这个问题:
字符串
生成的文件:
型
输出量:
型
afdcj2ne5#
Python可以正确处理CSV数据。下面的程序使用字典(map)将每个值(col 2)存储在一个列表中,该列表与最后一个看到的类别(col 1)相关联。这种最后一次看到的方法允许缺少类别的值与最后一个类别(在它上面)相关联:
字符串
我们可以用两组循环来检查字典:
型
我得到:
型
然后使用一组类似的循环将类别值写入它们自己的文件。我根据类别进行一些基本的文件名清理:
型
然后我得到一个文件列表,比如:
型
output-2-Bathroom-home room.txt看起来像:
型
vybvopom6#
为了完整起见,您可以使用普通的
bash
来完成此操作(因为只有1000行,性能应该不是问题)。请注意,与其他基于awk的答案不同,这实际上创建了您想要的文件,即使是在一个(例如,2-Police/Enforcement.txt
)。和其他答案一样,如果你有多个-行记录在输入CSV中。如果第二行的第一个字段为空,则将创建的文本文件为.txt
。将以下内容放入文件中(例如,~/bin/csv2txt
):字符串
使其可执行:
型
然后:
型
或者,如果您的
PATH
中已经有~/bin
:型
免责声明:
tail -n+2 "$1" | while IFS=, read -r tmp b; do
:我们使用tail
删除CSV文件的第一行,并使用Input Field Separator将其他行传输到while
循环(IFS
)设置为逗号。对于每行,我们存储第一个字段(在第一个逗号之前)在tmp
和该行的其余部分(在第一个逗号之后)。我们使用read
的-r
选项来禁止反斜杠转义任何字符。a="${tmp:-$a}"
:如果tmp
不为空,我们将其分配给a
,否则(例如在第3行,countless
),我们让a
未修改。[[ "$a" == */* ]] && mkdir -p "${a%/*}"
:如果a
包含一个斜杠(例如2-Police/Enforcement
),我们将创建相应的目录。[[ -f "$a.txt" ]] || printf '%s\n' "$a" > "$a.txt"
:如果目标文本文件("$a.txt"
)不存在,我们将在其中打印$a
。b="${b#\"}"
:我们从b
中删除任何前导"
。printf '%s\n' "${b%\"}" >> "$a.txt"
:我们从b
中删除任何尾随的"
,并将其值附加到目标文本文件中。zy1mlcev7#
要正确处理可能包含转义引号等的引号值,您需要一种具有适当CSV解析器的语言。
字符串
这里是一个稍微修饰的版本,涵盖了更多的角落情况。我假设第一列中的空单元格的例子应该被跳过,并且带有斜杠的标签应该会导致子目录。
型
演示:https://ideone.com/7akFvw
如果你想让第一行包含一个头,这会使代码复杂化(你需要检查文件是否已经存在),但我认为缺少头是一个特性,而不是一个bug。
如果你真的需要优化它的速度,你应该保持尽可能多的文件句柄打开,但如果你打开超过操作系统允许的数量(通常在20的顺序)后退。关闭并立即重新打开一个文件往往会慢得多。