资讯动态

Python txt列提取:从逐行读取到pandas进阶技巧

发布时间:2026/10/14 2:56:49 来源:尧图企业网站定制
日常用Python处理文本时最常遇到的一个需求就是“将txt某一列数据存入到变量列表”。这个需求看着很小但真落地时会牵扯出一串问题文件用什么分隔符、有没有表头、数据要不要转成数值、文件很大怎么办、提取完要不要去空去重。这篇文章就把这条链路从头到尾捋一遍从最简单的内置方法开始再到csv模块和pandas的进阶玩法最后把踩坑记录一起整理出来给正在跟txt文件较劲的读者一份可以直接抄作业的参考。这个需求适合谁看刚接触Python的初学者、做数据分析的日常用户、以及经常需要处理日志或配置文件的开发者。无论你是想把一列数值读进来做计算还是把一列名称读进来做匹配核心逻辑都一样定位文件、按规则切分、把目标列装进列表变量。掌握了这个套路后续不管文件长什么样都能顺手处理。1. 需求拆解为什么一个“小功能”值得认真设计先把这个看似简单的需求拆开看。用户说“把txt某一列数据存入到变量列表”这句话背后藏着几个关键决策点。第一个决策点是分隔符。txt文件不像excel那样有固定的单元格结构列和列之间靠什么隔开完全由生成文件的程序说了算。常见的是逗号、制表符Tab、空格也有用竖线或者分号的。分隔符不同代码里split的参数就不同。初学者最容易在这里栽跟头看到一个文件里列之间是连续空格直接用split( )去切结果发现切出来一堆空字符串。这个问题后面细讲。第二个决策点是数据类型。从文件里读出来的东西默认都是字符串哪怕内容是“3.14”也只是个文本。如果你想把这一列数据拿去做求和、平均、排序就必须转成float或int。转的时候又会遇到“脏数据”空行、表头、注释行这些都会导致转换报错。所以一个健壮的提取代码通常要包含类型转换和异常处理两部分。第三个决策点是文件规模。几千行的txt随便读几百万行的文件就不能再逐行append了内存会撑不住。这时候要么用生成器逐条处理要么直接上pandas让底层C代码替你去跑循环。很多人说“Python处理大文件慢”其实慢的是自己写的逐行Python循环pandas的read_csv在绝大多数场景下都要快一个数量级。第四个决策点是后续用途。提取出来的列表是只用来打印看一眼还是要参与数学运算是全部都要还是只要非空值要不要保持原始顺序这些决定了提取完之后要不要加过滤、去重、排序的步骤。把这几个点想清楚再动手写代码思路就会非常清晰。这也是为什么我说这个“小功能”值得认真设计——它几乎覆盖了文本处理和数据结构转换的所有基础知识点把这一题练会了很多类似问题都能一通百通。2. 基础实操一行行读取把某一列装进列表2.1 读取前的准备工作先搞清楚文件长什么样拿到一个txt文件我建议你第一件事不是写代码而是先把它打开看一眼。看什么看前五行确认三件事有没有表头、分隔符是什么、数据从第几行开始是有效内容。这一步省掉了后面大量的Debug时间。举个例子假设有一个文件data.txt内容是这样的日期,温度,湿度 2024-01-01,12.5,45 2024-01-02,13.2,48 2024-01-03,11.8,43这里分隔符是逗号第一行是表头有效数据从第二行开始。如果只想要温度这一列目标就是让列表变成[12.5, 13.2, 11.8]。还有一种常见情况是配置文件像下面这种name: alice age: 25 score: 92.5这种“键值对”格式其实也可以看作两列数据分隔符是冒号加空格。提取右侧的值就是一个很典型的“取某一列”需求。无论哪种情况思路都是一样的。2.2 最朴素的写法open、split、append最直接的实现方式是逐行读取、按分隔符切分、取索引、追加到列表。来看代码data [] with open(data.txt, r, encodingutf-8) as f: for line in f: line line.strip() # 去掉行尾换行符和首尾多余空格 if not line: # 跳过空行 continue parts line.split(,) # 按逗号切分成列表 value parts[1].strip() # 取第2列索引从0开始 data.append(float(value)) # 转成浮点数并存入列表这四行代码是整套逻辑的核心每一步都值得展开说一下。line.strip()很重要。直接for line in f读出来的每一行末尾都带\n如果这行数据正好是文件的最后一行末尾连换行符都没有。strip()会把换行符和行首行尾的空格一起清掉避免后面转换类型的时候报“could not convert string to float: 12.5这种错。line.split(,)的作用是把“2024-01-01,12.5,45”切成[2024-01-01, 12.5, 45]。注意返回的是一个列表索引用0开始所以第2列是parts[1]。float(value)是把字符串转成数值。如果这一列将来要做算术运算这一步不能省。但如果只是拿来做匹配、做标签保留字符串也没问题。with open(...) as f是Python推荐的打开文件方式它会在代码块执行完后自动关闭文件。很多人习惯直接f open(...)然后读最后忘了f.close()在Windows系统上会导致文件被占用删不掉也改不了。用with就不会有这个问题。把这段代码跑一遍得到的data就是一个列表[12.5, 13.2, 11.8]。打印一下看看print(data) print(sum(data) / len(data))第一行确认数据正确第二行顺手算个平均值验证类型转换生效了。2.3 跳过表头两种常见写法如果文件有表头上面那份代码会把“温度”这个字符串也试图转成float直接抛出ValueError。处理表头的方法很直接加一个计数器data [] with open(data.txt, r, encodingutf-8) as f: for line_num, line in enumerate(f): if line_num 0: continue line line.strip() if not line: continue parts line.split(,) data.append(float(parts[1].strip()))enumerate(f)给每一行编了个号第0行就是表头continue跳过。如果你要跳过的不是第一行而是前几行把if line_num 0改成if line_num n就行。还有一种用itertools.islice的写法代码看起来更简洁from itertools import islice data [] with open(data.txt, r, encodingutf-8) as f: for line in islice(f, 1, None): # 从第二行开始读 line line.strip() if not line: continue parts line.split(,) data.append(float(parts[1].strip()))islice的第二个参数是起始行索引第三个参数是结束位置None表示一直到末尾。两种写法效果一样我个人的习惯是行数少用if判断行数多、表头结构复杂用islice因为后者语义更清晰。2.4 分隔符是多个空格或Tab怎么办这是最容易踩坑的地方。看这个文件1 12.5 45 2 13.2 48 3 11.8 43列之间是用Tab分隔的但也可能是多个空格。如果你用split( )去切得到的是[1, , , 12.5, , 45]中间全是空字符串取索引取错位置类型转换又会报错。正确的做法是直接用line.split()不带任何参数。Python的split()不加参数时有一个特殊行为它会按任意空白字符切分包括空格、Tab、换行而且会把连续多个空白当成一个分隔符处理。对于“列之间用多个空格或者Tab对齐”的文件这个写法是首选。parts line.split() # 自动处理连续空格和Tab value parts[1] # 第二列如果分隔符是制表符Tab也可以显式写成line.split(\t)。需要说明的是这种按Tab切的方式不会合并连续的空格如果文件里有混排的情况还是用无参数的split()更省心。如果文件里既有Tab又有多个空格还有行首行尾多余空白最稳妥的方案是先用正则把所有空白归一化import re line re.sub(r\s, , line.strip()) parts line.split( )\s匹配一个或多个空白字符全部替换成单个空格然后再按空格切。这样做的好处是规则明确但坏处是正则匹配在小文件上无所谓几百万行的大文件里会有明显的性能开销。所以我的建议是简单场景用split()复杂到split()搞不定的时候才考虑正则。3. 进阶方案csv模块、pandas和批量处理3.1 txt其实也可以当csv读很多人不知道Python标准库里的csv模块不只是给.csv后缀的文件用的它处理任何“带分隔符的文本”都很好用。对于逗号分隔的txt直接这样写import csv data [] with open(data.txt, r, encodingutf-8) as f: reader csv.reader(f) next(reader) # 跳过表头 for row in reader: data.append(float(row[1].strip()))csv.reader返回的row是一个列表里面的每一项就是按分隔符切出来的字段。csv模块的好处是它对分隔符的处理更智能比如字段内带引号的情况csv.reader会自动处理引号内的逗号而split(,)会把引号里的逗号也切开导致列错位。如果你要支持非逗号分隔符给csv.reader传入dialect或delimiter参数reader csv.reader(f, delimiter\t) # 按Tab分隔 reader csv.reader(f, delimiter;) # 按分号分隔csv模块还有一个header相关的便捷功能DictReader可以把第一行当作字段名然后通过字段名来取列import csv data [] with open(data.txt, r, encodingutf-8) as f: reader csv.DictReader(f) # 自动把第一行当表头 for row in reader: data.append(float(row[温度].strip()))这种写法最大的优势是可读性强。row[温度]一眼就能看出取的是哪一列不会出现parts[1]到底代表什么列还得回去数一遍的烦恼。在列很多、列顺序不固定的时候DictReader是首选。3.2 文件大了直接上pandas如果文件有几十万行甚至更大再用Python逐行循环就有点吃力了。这时候最省力的方案是pandas。import pandas as pd df pd.read_csv(data.txt, sep,) data df[温度].tolist()这两行做的事情等价于前面十几行逐行读取代码。read_csv读取文件之后把指定列转成列表一行搞定。如果要去掉缺失值data df[温度].dropna().tolist()如果数据文件有自定义的列名或者没有表头df pd.read_csv(data.txt, sep,, headerNone, names[日期, 温度, 湿度]) data df[温度].tolist()headerNone表示文件没有表头names手动指定列名。这个组合很常用尤其是面对那种光有数据不带标题的txt。如果只想读某一列不加载其他列用usecols参数内存占用会比全表读小很多df pd.read_csv(data.txt, sep,, usecols[温度]) data df[温度].tolist()usecols只加载指定的列对大文件是很实用的优化。另外一个常用参数是skiprows跳过文件前几行df pd.read_csv(data.txt, sep,, skiprows1)skiprows1等价于手动跳过表头。如果文件开头有一大段说明文字比如前五行是注释写skiprows5就行。这个参数做日志文件清洗的时候特别常用。那到底什么时候用标准库什么时候用pandas我个人的分界线是文件大小。小于几万行用自带方法完全没问题超过这个量级或者要做的操作不止是提取一列、后面还有过滤、分组、聚合的时候直接上pandas避免自己造轮子。3.3 批量处理一百个txt提取同一列实际工作中经常遇到这种需求文件夹里有几十上百个txt每个文件结构一样需要把它们某一列的数据都提取出来汇总。这时一个循环加一个路径拼接就能搞定。import glob import pandas as pd all_data [] for file_path in glob.glob(data/*.txt): df pd.read_csv(file_path, sep,) all_data.append(df[温度].tolist()) # all_data 是一个嵌套列表每项对应一个文件如果希望所有文件的数据拼成一个一维列表用sum或者列表推导式展平flat_data [value for file_data in all_data for value in file_data]或者追求效率直接用itertools.chainfrom itertools import chain flat_data list(chain.from_iterable(all_data))如果文件路径分散在不同的子目录里用glob.glob(**/*.txt, recursiveTrue)可以递归匹配。批量处理时最好先统计一下实际匹配到几个文件再开始处理避免路径写错导致“批量处理”变成“批量空转”——这个坑我踩过代码跑完发现列表是空的回头一查glob匹配到的文件数为0纯属路径级目录配错了。3.4 提取完还能干什么去空、去重、排序、统计拿到了列表往往不是终点后面还有一些常规处理。把常用的几个列出来当作字典查。去掉空字符串和空值data [x for x in data if x ! ]这个方法简单但要注意如果data里存的是float判断空值要用isnanimport math data [x for x in data if not math.isnan(x)]去重并保持顺序# 用dict.fromkeys去重Python 3.7保证顺序 data list(dict.fromkeys(data))先转set再去重速度快但会丢失顺序因为set是无序的。排序data.sort() # 原地排序 sorted_data sorted(data) # 返回新列表对数值列表来说sort默认从小到大。如果是从大到小写data.sort(reverseTrue)。统计平均值和分布import statistics avg statistics.mean(data) median statistics.median(data) stdev statistics.stdev(data)这些操作配合前面从txt提取出来的列表基本能覆盖一个数据分析流程的前半段。3.5 编码问题打开文件乱码的三种应对方案txt文件的编码是另一个让人头大的问题。Windows上常见的默认编码是GBK很多老程序生成的txt文件要指定encodinggbk才能正确读取。而Linux和macOS上更常见的是UTF-8。一段稳妥的打开文件代码是with open(data.txt, r, encodingutf-8) as f: ...如果打开时报错UnicodeDecodeError说明文件编码不是UTF-8最简单的应对方案是尝试改成gbkwith open(data.txt, r, encodinggbk) as f: ...还有一个比较粗暴但实际很常用的兜底做法不指定具体编码而是在open时用errors参数忽略无法解码的字符with open(data.txt, r, encodingutf-8, errorsignore) as f: ...errorsignore会把无法解码的字节直接跳过。这对“数据本身不关心中文内容”的场景很好用但如果这一列恰好在乱码区提取出来的数据就会缺字符。更强的方案是用errorsreplace把没法解码的字符替换成数据长度能保持一致但内容可能不准确。具体选哪个取决于你的数据容错要求。还有一种检测编码的技巧用chardet库自动判断import chardet with open(data.txt, rb) as f: raw f.read(10000) # 只读前1万个字节做检测 result chardet.detect(raw) print(result[encoding])检测出来之后再按结果打开。这个方法适合应付那种完全不知道编码是什么的“野文件”但它也不是百分百准确检测结果要人工确认一下。4. 常见问题与排查技巧实录这部分整理一下我实际处理txt文件时遇到过的典型问题每条都是踩过坑换来的经验。先看一个最常见的Bug文件明明有数据提取出来的列表却是空的。with open(data.txt, r, encodingutf-8) as f: for line in f: parts line.split(,) data.append(parts[1])然后print(data)发现是[]。排查思路是这样的第一步确认data这个列表在取列之前是空的并且append执行了没有。加一行print(parts)看看每一行切分出来的结果。通常这一步就能发现问题所在有的文件分隔符不是逗号而是中文逗号“”有的文件是空格对齐split(,)切出来永远只有一段parts[1]直接抛IndexError。如果加了异常处理把IndexError吞掉了列表就一直是空。遇到这种情况最有效的办法就是先打印原始行的repr信息for line in f: print(repr(line))repr会显示字符串最原始的样子包括看不见的换行符、Tab、行尾空格。看完输出对分隔符到底是什么就有了准确的认识。第二个常见问题是类型转换报错data.append(float(parts[1].strip())) # ValueError: could not convert string to float: 12.5\r这个报错很典型\r是Windows系统里的回车符。Windows换行是\r\nLinux/macOS换行是\n。在Windows上打开一个文本文件再另存为别的格式有时候行尾会混进\r。strip()会去掉字符串首尾的空白字符而\r在Python字符串里也算空白字符所以正常情况下strip()能去掉。但如果用了split( )把整行按空格切换行符还挂在最后一个字段上这时再去strip就来得及。解决方式有两种一是用line.strip()把整行首尾清干净之后再去split二是已经在切分之后发现字段带\r在字段上再调一次strip()。我的习惯是优先处理整行代码更简洁。第三个问题是数据里混入了空行。文件末尾经常有一个空行或者中间有一行是空的。如果不加判断直接取值float()必然报错。这提醒我们每次读文件跳过空行这个步骤不要省。if not line.strip(): continue如果一行里只有分隔符而没有数据比如一行a,,b这种跳过空行的判断是拦不住的。这时要判断切分出来的parts长度是否符合预期if len(parts) 2: continue这种“列数不足就跳过”的写法在处理日志类txt文件时尤其重要因为日志文件经常有残缺行。第四个问题是文件明明存在却报找不到文件“FileNotFoundError”。这个通常不是代码的问题而是路径问题。写相对路径的时候要注意当前工作目录。在终端里跑代码工作目录是当前终端所在的目录在IDE里跑代码工作目录可能是项目根目录。同样的相对路径在不同环境下指向的可能不是同一个文件。一个实用的调试方法import os print(os.path.exists(data.txt)) print(os.getcwd())如果exists返回False说明文件路径不对。这时把相对路径改成绝对路径或者用os.path.join(os.path.dirname(__file__), data.txt)来定位脚本文件同目录下的数据文件。后面这种写法最保险不管在哪里执行脚本都能找到正确路径。第五个问题是文件太大内存爆掉。读取一个几百MB的txt如果一次性readlines读进来内存占用可能直接让程序崩掉。处理大文件的正确策略是保持逐行迭代不要让文件全部加载到内存# 不推荐文件过大时内存会爆 lines f.readlines() for line in lines: ... # 推荐边读边处理每行处理完就释放 for line in f: ...for line in f本身就是逐行读取的它不会把整个文件读进内存底层是一个生成器一边读一边吐。对于大文件这是最基本也最有效的内存优化手段。如果再考虑极致性能可以用csv模块或pandas它们在底层做了很多缓冲优化处理速度比手动逐行迭代更快。第六个问题是提取出来的列表里有意外的前后空格。比如数据是 12.5 这种转成float之前一定先strip否则报错或者得到错误的值。我一直强调strip的原因就在这里它是文本处理里最基础但最容易被忽略的一步。最后再说一个我自己常用的调试习惯写完提取代码不要直接去算平均值或者做排序先print(data[:5])看看前5个元素再print(len(data))看看总个数。这两个输出能快速验证提取逻辑是否正确。等确认数据本身没问题了再继续往下写处理逻辑。这个习惯让我少走很多弯路。结尾根据我个人的经验处理txt列提取这类任务代码本身并不难真正决定效率的是前面两步先把文件结构和格式看清楚再想清楚提取之后要拿数据做什么。在动手敲代码前花五分钟做这两件事后续的调试时间能省掉一大半。另外建议志同道合的读者们养成一个习惯用print(repr(line))去查看原始行用带strip的写法去处理字段用with来管理文件资源这三个小细节组合起来能让你的文本处理代码在健壮性上有一个明显的提升。把这些基础动作练成肌肉记忆后面无论是遇到CSV还是日志还是配置文件都能游刃有余地应对。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑