【问题标题】:How to convert the first letter of all words of several columns of a csv file to uppercase while making the rest of the letters lowercase?如何将csv文件几列的所有单词的第一个字母转换为大写,同时将其余字母变为小写?
【发布时间】:2019-10-07 06:51:54
【问题描述】:

重击 4.4.0 Ubuntu 16.04

我在 CSV 文件中有几列都是大写字母,有些是小写字母。有些列只有一个单词,而其他列可能有 50 个单词。此时,我使用 2 个命令逐列转换,当文件有 50k 行时,这对服务器来说是相当繁重的。

例子:

#-- Place the header line in a temp file
head -n 1 "$tmp_input1" > "$tmp_input3"
#-- Remove the header line in orginal file
tail -n +2 "$tmp_input1" > "$tmp_input1-temp" && mv "$tmp_input1-temp" "$tmp_input1"
#-- Change the words in the 11th column to lower case then change the first leter to upper case
awk -F"," 'BEGIN{OFS=","} {$11 = tolower($11); print}' "$tmp_input4" > "$tmp_input5"
sed -i "s/\b\(.\)/\u\1/g" "$tmp_input5"
#-- Change the words in the 12th column to lower case then change the first leter to upper case
awk -F"," 'BEGIN{OFS=","} {$12 = tolower($12); print}' "$tmp_input5" > "$tmp_input6"
sed -i "s/\b\(.\)/\u\1/g" "$tmp_input6"
#-- Change the words in the 13th column to lower case then change the first leter to upper case
awk -F"," 'BEGIN{OFS=","} {$13 = tolower($13); print}' "$tmp_input6" > "$tmp_input7"
sed -i "s/\b\(.\)/\u\1/g" "$tmp_input7"
cat "$tmp_input7" >> "$tmp_input3"

是否可以在一个命令中执行多个列?

以下是 csv 文件的示例:

"dealer_id","vin","conditon","stocknumber","make","model","year","broken","trim","bodystyle","color","interiorcolor","interiorfabric","engine","enginedisplacement","engineaspiration","engineText","transmission","drivetrain","mpgcity","mpghighway","mileage","cylinders","fuelconditon","optiontext","description","titlestatus","warranty","price","specialprice","window_sticker_price","mirrorhangerprice","images","ModelCode","PackageCodes"
"JOHNVANC04A","2C4RC1N73JR290946","N","JR290946","Chrysler","Pacifica","2018","","Hybrid Limited FWD","Mini-van, Passenger","Brilliant BLACK Crystal PEARL Coat","","..LEATHER SEATS..","V6 Cylinder Engine","3.6L","","","AUTOMATIC","FWD","0","0","553","6","H","..1-SPEED A/T..,..AUTO-OFF HEADLIGHTS..,..BACK-UP CAMERA..,..COOLED DRIVER SEAT..,..CRUISE CONTROL..","======KEY FEATURES INCLUDE: . LEATHER SEATS. THIRD ROW SEAT. QUAD BUCKET SEATS. REAR AIR. HEATED DRIVER SEAT.","","0","41680","","48830","","http://i.autoupktech.com/c640/9c40231cbcfa4ef89425d108e4e3a410.jpg",http://i.autoupnktech.com/c640/9c40231cbcfa4ef89425d108e4e3a410.jpg","RUES53","AAX,AT2,DFQ,EH3,GWM,WPU"

这是上述列的一个 sn-p 改进

Column 11 should be - "Brilliant Black Crystal Pearl Coat"
Column 13 should be - "Leather Seats"
Column 16 should be - "Automatic"
Column 23 should be - "1-Speed A/T,Auto-Off Headlights,Back-up Camera"
Column 24 should be - "Key Features Include: Leather Seats,Third Row Seat"

请记住,不能删除列周围的双引号。我只需要转换某些列而不是整个文件。这是转换后的第 11、13、16、23 和 24 列的示例。

"Brilliant Black Crystal Pearl Coat","Leather Seats","Automatic","1-Speed A/T,Auto-Off Headlights,Back-up Camera","Key Features Include: Leather Seats,Third Row Seat"

【问题讨论】:

  • 请发布示例输入文件和您想要的预期输出。我认为使用 gnu sed 可以。 1.把所有东西都小写。 2.然后sed 's/\W./\U&/g'
  • 嘿,你需要这个:“这是一个字段”,“这是另一个字段”或这个“这是一个字段”,“这是另一个字段”?????
  • 我知道这是题外话,但使用 Python 的 str.title() 会轻而易举。
  • 即:'just,a,LONG,LIST of SOme,RaNdOm wORDS'.title(),结果为'Just,A,Long,List Of Some,Random Words'
  • 好的...让我改进它以处理多个单词

标签: bash awk sed


【解决方案1】:

只是添加另一个选项,这是一个仅使用 sed 的衬里:

sed -i -e 's/.*/\L&/' -e 's/[a-z]*/\u&/g' filename

这是一个概念证明:

$ cat testfile 
jUSt,a,LONG,list of SOME,RAnDoM WoRDs
ANother LIne
OneMore,LiNe
$ sed -e 's/.*/\L&/' -e 's/[a-z]*/\u&/g' testfile 
Just,A,Long,List Of Some,Random Words
Another Line
Onemore,Line
$ 

如果您只想转换CSV 文件的标题(第一行),只需在两种搜索模式上将s 替换为1s

你可以在这里找到一篇解释魔法的优秀文章:sed – Convert to Title Case

【讨论】:

  • 我认为整个文件都需要转换。示例代码确实有head -1,但精化列的 sn-p 显示来自第二行的数据。如果您同意,请将 1s 替换为 s。无论如何,我都赞成你的好答案和诚实的链接。
  • @WalterA 刚刚做到了,感谢您的支持。我在等待 OP 说出他真正想要的东西。 :-)
  • 那里有一个图像列,所以我无法更改整个文件。只有某些列。列仍然需要用双引号括起来并用逗号分隔。抱歉,如果我的 OP 不清楚。
【解决方案2】:

这是 Python 3 中的另一种替代方法(我知道这里是题外话):

import csv
from pathlib import Path

infile = Path('infile.csv')
outfile = Path('outfile.csv')

titled_cols = [10, 12, 15, 22, 23]
titled_data = []

with infile.open() as fin, outfile.open('w', newline='') as fout:
    for row in csv.reader(fin, quoting=csv.QUOTE_ALL):
        for i,col in enumerate(row):
            if i in titled_cols:
                col = col.title()
        titled_data.append(row)    
    csv.writer(fout, quoting=csv.QUOTE_ALL).writerows(titled_data)

只需在titled_cols 上定义您希望标题大小写的列(列具有从零开始的索引),它就会执行您想要的操作。

我猜infileoutfile 是不言自明的,outfile 将包含原始文件的修改版本。

希望对你有帮助。

【讨论】:

  • 这会在空列处停止。示例:如果我想转换第 8、10、11 列但第 9 列是空的第 10 列,则不会转换 11。如此亲密的伙伴。
  • 该死!给我一个更完整的csv 文件样本,我会尝试解决这个问题。根据您向我们展示的内容,我只是将其从脑海中抹去。
  • 事实上,我认为修复是微不足道的:我想我们也只需将quoting=csv.QUOTE_ALL 传递给csv.reader()。虽然没有测试。你能试一试并告诉我们结果吗?如果可行,我将使用该修改编辑答案。
  • 在过去的几天里,我在这个线程上收到了很多知识和信息。每个人如何聚在一起并帮助每个人,这真是太神奇了。现在你的脚本是正确的。我总是可变完整路径,所以我从我的 shell 脚本中调用了你的 python 脚本。我在 2 秒内处理了 5 k 行。向你的方向倾斜一个冷的。非常感谢。
  • @CuriousSam,我很高兴知道它有效。欢迎来到 Python 世界,伙计。 :-)
【解决方案3】:

您可以创建一个用户定义的函数并将其应用于您需要修改的列。

awk -F, 'function toproper(s) { return toupper(substr(s, 1, 1)) tolower(substr(s, 2, length(s))) } {printf("%s,%s,%s,%s\n", toproper($1), toproper($2), toproper($3), toproper($4));}'

输入:

FOO,BAR,BAZ,ETC

输出:

Foo,Bar,Baz,Etc

【讨论】:

    【解决方案4】:

    假设csv文件的字段没有用双引号引起来, 这意味着我们可以简单地用逗号和空格分割记录,如何 关于Perl 解决方案:

    perl -pe 's/(^|(?<=[,\s]))([^,\s])([^,\s]*)((?=[,\s])|$)/\U$2\L$3/g' input.csv
    

    输入.csv:

    Bash,4.4.0,Ubuntu,16.04
    I have several columns in a CSV file,that, are, all capital letters
    and  some are lowercase.
    Some columns have only,one,word,while others may have 50 words.
    

    输出:

    Bash,4.4.0,Ubuntu,16.04
    I Have Several Columns In A Csv File,That, Are, All Capital Letters
    And  Some Are Lowercase.
    Some Columns Have Only,One,Word,While Others May Have 50 Words.
    

    【讨论】:

    • 我不确定,但我的印象是 OP 只想转换每个文件的第一行(我猜是他们命令中的 head -1)。
    • @accdias 感谢您的评论。我理解你的观点,尽管 OP 的要求仍然模棱两可。如果要求明确,我将能够更新我的答案。
    • 是的,他们是。我上面的示例显示了所有列双引号。抱歉,如果不清楚。
    【解决方案5】:

    这个版本使用AWK来完成这项工作:

    这是命令(将 file 更改为您的文件名)

    awk  -F"," 'BEGIN{OFS=","}{ for (i=1; i<=NF; i++) { $i=toupper(substr($i,1,1))""tolower(substr($i,2,length($i)))}print $0}' file | awk -F" "  'BEGIN{OFS=" "} { for (i=1; i<=NF; i++) { $i=toupper(substr($i,1,1))""substr($i,2,length($i))}print $0}'
    

    测试:

    cat file
    pepe is cool,ASDASD ASDAS,and no podpoiaops
    awk  -F"," 'BEGIN{OFS=","}{ for (i=1; i<=NF; i++) { $i=toupper(substr($i,1,1))""tolower(substr($i,2,length($i)))}print $0}' file | awk -F" "  'BEGIN{OFS=" "} { for (i=1; i<=NF; i++) { $i=toupper(substr($i,1,1))""substr($i,2,length($i))}print $0}'
    Pepe Is Cool,Asdasd Asdas,And No Podpoiaops
    

    解释

    • BEGIN{OFS=","} 告诉 awk 如何输出该行。
    • for 语句使用 NF,即为 每行的字段数
    • substr分割并改变字段的第一个字母,并再次分配给它的行值
    • 打印所有行print $0
    • 最后,第二个awk 将第一个示例中创建的行分开,但这次以空格作为分隔符。这样,它会检测文件中所有不同的单词,并更改它们的每个第一个字符。

    希望对你有帮助

    【讨论】:

    • 正如OP中所说,某些列可能是多个单词。由于您使用,作为字段分隔符,它会处理同一列中以空格分隔的单词吗?
    • 它将改变字段的第一个字母。我正是在问这个。更改第一个字母的空格无关紧要。如果我们需要更改所有单词,也很容易
    • 正如我所怀疑的,因此问题。 :-) 让我们看看作者真正想要什么。
    猜你喜欢
    • 1970-01-01
    • 2010-12-28
    • 2021-09-16
    • 2013-02-04
    • 1970-01-01
    • 2021-04-29
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    相关资源
    最近更新 更多