Tcl 脚本读取复杂CSV文件

用 tcl/tk 写了个测试工具,需要用tcl 脚本读取csv 文件。但复杂的csv 文件中,每个单元格可能包含逗号,双引号,换行符,双引号中又有换行符等等情况,导致读取困难。网上找到的一些例子,大多是逐个读取单个字符,用了一段时间,感觉效率差了点。研究了一下,自己写了 tcl 读csv 文件的代码,如下:

proc readCSV { channel { header 1 } { symbol , }} {
	set quote 0	
	set data [ split [ read $channel nonewline ] "\n" ]
	foreach line $data {
		set quote [ expr { $quote + [ regexp -all \" $line ]}]
		if { [ expr { $quote % 2 }] == "0" } {
			set quote 0
			append row_temp $line
			set row_temp [ split $row_temp , ]	
			foreach section $row_temp {
				set quote [ expr { $quote + [ regexp -all \" $section ]}]
				if { [ expr { $quote % 2 }] == "0" } {
					append cell_temp $section
					set cell_temp [ regsub {"(.*)"} $cell_temp {\1} ]
					lappend cell $cell_temp
					unset cell_temp
					set quote 0
				} else {
					append cell_temp $section$symbol
				}
			}
			lappend final [ regsub -all {""} $cell \" ]
			unset cell
			unset row_temp
		} else {
			append row_temp $line\n
		}
	}
	# generate array if needed, or return $final here
	set row [ llength $final ]
	set column [ llength [ lindex $final 0 ]]
	if { $header == 1 } {
		for { set i 0 } { $i < $row } { incr i } {		
			for { set j 0 } { $j < $column } { incr j } {
				set csvData([ lindex [ lindex $final 0 ] $j ],$i) [ lindex [ lindex $final $i ] $j ]
			}
		}
	} else {
		for { set i 0 } { $i < $row } { incr i } {		
			for { set j 0 } { $j < $column } { incr j } {
				set csvData($i,$j) [ lindex [ lindex $final $i ] $j ]
			}
		}
	}
	return [ array get csvData ]
}

函数返回一个数组,默认指定csv文件中第一行作为Header,分隔符为",",可变更。

能够处理csv文件中包含的 ",", "'", "\n" 字符。

 

Example:

下面是以Header & line number的方式输出某单元格数据:

set csv [ open c:/testcase.csv {RDWR} ]
array set csvData [ readCSV $csv ]
puts $csvData(Name,1)    ;# assume there is a cell containing "Name" at first row.

下面是以row number & line number方式输出某单元格数据:

set csv [ open c:/testcase.csv {RDWR} ]
array set csvData [ readCSV $csv 0 ]
puts $csvData(3,1)   

 

Efficency:
经测试,处理 2000 x 4 容量的测试用例文件,用时100ms左右。

-----------------------------------

CPU: Dual-Core 3.20GHz

Memory: 2G

System Type: 32bit

-----------------------------------

tcl 里有个专门处理csv文件的包,叫csv,对比了一下效率。如果同样返回处理后的数据列表,这个函数处理速度会快一点。

csv package的使用方法:

package require csv
package require struct::queue

set csv [ open c:/testcase.csv {RDWR} ]

::struct::queue q
::csv::read2queue $csv q
set final [ q peek [ q size ]]

CappacityreadCSVcsv packagefile size
2000*4103ms170ms768KB
2000*8200ms335ms1534KB
2000*16382ms770ms3065KB
2000*32760ms2088ms6127KB
2000*641501ms6411ms12252KB
2000*1282995ms21841ms24501KB

Output:

所输出的数据,与在Excel 中看到的csv 文件内容相同。


类的形式:

package require Itcl

itcl::class readCSV {
	common final
	common anchor 1
	constructor { path } {
		set quote 0
		set channel [ open $path {RDWR} ]
		set data [ split [ read $channel nonewline ] "\n" ]
		close $channel
			foreach line $data {
				set quote [ expr { $quote + [ regexp -all \" $line ]}]
				if { [ expr { $quote % 2 }] == "0" } {
					set quote 0
					append row_temp $line
					set row_temp [ split $row_temp , ]	
					foreach section $row_temp {
						set quote [ expr { $quote + [ regexp -all \" $section ]}]
						if { [ expr { $quote % 2 }] == "0" } {
							append cell_temp $section
							set cell_temp [ regsub {"(.*)"} $cell_temp {\1} ]
							lappend cell $cell_temp
							unset cell_temp
							set quote 0
						} else {
							append cell_temp $section,
						}
					}
					lappend final [ regsub -all {""} $cell \" ]
					unset cell
					unset row_temp
				} else {
					append row_temp $line\n
				}
			}
	}
	
	method getCell { row col } {
		return [ lindex [ lindex $final $row ] $col ]
	}
	
	method getValue { header } {
		set col [ lsearch [ lindex $final 0 ] $header ]
		return [ getCell $anchor $col ]
	}
	
	method next { } {
		if { [ done ] == 0 } {
			incr anchor
		}
	}
	
	method pre { } {
		if { $anchor > 1 } {
			incr anchor -1
		}
	}
	
	method end { } {
		set anchor [ expr {[ llength $final ]-1}]
	}
	
	method done { } {
		if { $anchor == [ expr {[ llength $final ]-1} ]} {
			return 1
		} else {
			return 0
		}
	}
	
	method reset { } {
		set anchor 1
	}
	
}	

NameAgeAddress
Zhang_san13Address1:
1. aaaaa
2. aaad "bbbb",
3. bacad,
adfa"aaa".
Li_si14Address2, xxxx
aaaa"
bbbbb".,
Wang_wu15Address3

Example:

readCSV f c:/csvfile.csv
f getValue Name
output:

Zhang_san

f next
f getValue Name
output:

Li_si

f pre
f getValue Name
f end
f getValue Name
f getCell 1 0
output:

Zhang_san

Wang_wu

Zhang_san

  • 3
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值