如何为 txt 文件中的每 2 行创建一个 csv 行

How to make a csv row for each 2 lines in a txt file

我有一个这样的文本文件:

Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz
Sclerophthora macrospora virus A
Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz
Influenza B virus RNA
Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz
Tomato mottle virus

我需要得到这样的 csv 文件:

Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz,Sclerophthora macrospora virus A
Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz,Influenza B virus RNA
Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz,Tomato mottle virus

因为稍后我想像元组一样使用它来查找压缩文件,读取它并获得名称如下的最终文件:

Viruses/GCF_000837105.1/Tomato mottle virus.fna

我只需要学习如何做问题的第一部分。它可以通过:

如有任何帮助,我们将不胜感激。这对我来说很难完成,因为原始文件名很乱。

我试过这个:

sed -z 's/\n/,/g;s/,$/\n/' multi_headers

然而它把逗号放在所有 \n.

这个呢

with open('test.txt') as f:
    data = f.read().split('\n')
new_data = []

for a in range(0,len(data),2):
    new_data.append(data[a]+','+data[a+1]+'\n')
    
with open('result.txt','w') as f:
    f.writelines(new_data)

with open('test.txt') as f_read, open('result.txt','w') as f_write:
    data = f_read.read().split('\n')
    new_data = []

    for a in range(0,len(data),2):
        new_data.append(data[a]+','+data[a+1]+'\n')

    f_write.writelines(new_data)

使用sed

$ sed '/^Viruses/{N;s/\n\(.*\)/,/}' multi_headers
Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz,Sclerophthora macrospora virus A
Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz,Influenza B virus RNA
Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz,Tomato mottle virus

  • /^Viruses/ - 匹配以字符串 Viruses

    开头的行
  • {N; - Read/append下一行输入模式space.

  • s/\n\(.*\)/,/ - 从模式 space 中删除 \n 并将其替换为逗号 ,

Bash

你可以做一个paste(感谢@glenn jackman 指出我之前无用的cat)。

# or cat mytext.txt | paste -d "," - -
paste -d "," - - < mytext.txt 

Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz,Sclerophthora macrospora virus A
Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz,Influenza B virus RNA
Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz,Tomato mottle virus

R

R函数也是paste,加上sapply

mytext <- scan("mytext.txt", character(), sep = "\n")

sapply(seq(1, length(mytext), 2), function(x) paste(mytext[x], mytext[x + 1], sep = ","))
[1] "Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz,Sclerophthora macrospora virus A"
[2] "Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz,Influenza B virus RNA"           
[3] "Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz,Tomato mottle virus"   

在每个 Unix 机器上的任何 shell 中使用任何 awk,一次只在内存中存储 1 行,因此无论输入文件有多大,它都可以工作:

$ awk '{ORS=(NR%2 ? "," : RS)} 1' file
Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz,Sclerophthora macrospora virus A
Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz,Influenza B virus RNA
Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz,Tomato mottle virus

上面的少量代码发生了很多事情,所以这里有一个解释:

  • ORS 是内置变量,包含要在每个输出记录(本例中为 record = line)末尾打印的字符串,默认为换行符。
  • RS 是包含分隔每个输入记录的字符串(或正则表达式)的内置变量,默认为换行符。
  • NR 是包含当前 record/line 数字的内置变量,因此 NR%2 对于奇数记录为 1,对于偶数记录为 0。
  • NR%2 ? "," : RS 是一个 ternary expression,对于奇数行,,\n(或者您将 RS 设置为的任何其他内容,例如 \r\n) 对于偶数。
  • 1 为真条件,导致执行打印当前记录的默认操作。

所以上面的脚本说“如果当前行号是奇数,则在末尾用 , 打印它,否则在末尾用换行符打印它”,因此它用一个,之间。

一个简单的writerows():

import csv

with open("text.txt", "r") as f:
    with open("data.csv", "w", newline="") as w:
        writer = csv.writer(w)
        # May want to add headers
        writer.writerow(["Heading1", "Heading2"])
        x = f.readlines()
        writer.writerows([x[i:i+2] for i in range(0, len(x), 2)])

产生:

Heading1,Heading2
Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz,Sclerophthora macrospora virus A
Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz,Influenza B virus RNA
Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz,Tomato mottle virus

另一种 R 方法,依赖向量回收。

t = readLines("txt.txt")
paste0(t[c(T, F)], ",", t[c(F, T)]) |> writeLines("txt.csv")

或最终文件名

t = readLines("R/txt.txt")
sub("(?<=\.\d).*", "", t, perl = T) |>
  (\(.) paste0(.[c(T, F)], "/", .[c(F, T)], ".fna"))()

#> [1] "Viruses/GCF_000820355.1/Sclerophthora macrospora virus A.fna"
#> [2] "Viruses/GCF_000820495.2/Influenza B virus RNA.fna"           
#> [3] "Viruses/GCF_000837105.1/Tomato mottle virus.fna"  

简单python3解决方案,令file.txt内容为

Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz
Sclerophthora macrospora virus A
Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz
Influenza B virus RNA
Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz
Tomato mottle virus

script.py

with open("file.txt","r") as f:
    for inx, line in enumerate(f):
        print(line.rstrip(), end='\n' if inx%2 else ',')

然后

python script.py

输出

Viruses/GCF_000820355.1_ViralMultiSegProj14361_genomic.fna.gz,Sclerophthora macrospora virus A
Viruses/GCF_000820495.2_ViralMultiSegProj14656_genomic.fna.gz,Influenza B virus RNA
Viruses/GCF_000837105.1_ViralMultiSegProj14079_genomic.fna.gz,Tomato mottle virus

说明:我使用 .rstrip 丢弃尾随换行符,然后根据奇数行或偶数行分别应用 \n, 作为行尾。请注意,enumerate 默认从 0 开始,而不是 GNU AWK 从 1 开始。请注意,在文件句柄中使用 for ... 确实会阻止一次加载整个文件,因此该解决方案也可用于文件大于可用 RAM space.

这可能对你有用(GNU sed 和粘贴):

sed 'N;s/\n/,/' file

将下一行追加到当前行,然后用逗号替换换行符。

或:

paste -sd',\n' file

将文件粘贴为一个长字符串,用逗号替换每隔一个换行符。

要在混合中添加另一种解决方案,您还可以使用 xargs 并将输入行按 2 分组,然后在每个输出行中先将 space 替换为“,”。

xargs -n2 -d'\n' -a input.txt | sed 's/ /,/'