Python實(shí)現(xiàn)CSV文件編碼轉(zhuǎn)換工具(轉(zhuǎn)成UTF-8格式)
背景
一個(gè)支持拖拽操作的 CSV 文件批量編碼轉(zhuǎn)換工具,可以將各種編碼的 CSV 文件統(tǒng)一轉(zhuǎn)換為 UTF-8 編碼,解決中文亂碼問(wèn)題。
功能特點(diǎn)
多種文件添加方式
- 拖拽支持:直接將 CSV 文件或包含 CSV 的文件夾拖拽到程序窗口
- 單文件選擇:支持選擇單個(gè)或多個(gè) CSV 文件
- 文件夾批量:選擇文件夾,自動(dòng)收集其中的所有 CSV 文件
智能文件管理
- 自動(dòng)去重:重復(fù)添加的文件會(huì)被自動(dòng)過(guò)濾
- 列表管理:支持移除選中文件、清空列表等操作
- 實(shí)時(shí)反饋:狀態(tài)欄顯示當(dāng)前文件數(shù)量和操作結(jié)果
強(qiáng)大的編碼兼容性
- 多編碼支持:自動(dòng)嘗試 MBCS、ANSI、UTF-8、UTF-8-SIG 等編碼
- 智能識(shí)別:按優(yōu)先級(jí)依次嘗試,最大程度避免亂碼
- 統(tǒng)一輸出:所有文件統(tǒng)一轉(zhuǎn)換為 UTF-8 編碼
現(xiàn)代化界面
- 美觀布局:使用 ttk 控件,界面清晰美觀
- 進(jìn)度可視化:實(shí)時(shí)進(jìn)度條顯示轉(zhuǎn)換進(jìn)度
- 狀態(tài)提示:底部狀態(tài)欄顯示當(dāng)前操作狀態(tài)
系統(tǒng)要求
Python 版本:3.7 或更高版本
操作系統(tǒng):Windows、macOS、Linux(推薦 Windows)
可選依賴:tkinterdnd2(用于拖拽功能)
安裝與運(yùn)行
基礎(chǔ)運(yùn)行
python main.py
啟用拖拽功能(推薦)
pip install tkinterdnd2 python main.py
提示:如果未安裝 tkinterdnd2,程序會(huì)自動(dòng)降級(jí)運(yùn)行,并在界面提示安裝方法。
使用指南
步驟 1:添加文件
有三種方式添加需要轉(zhuǎn)換的 CSV 文件:
1.拖拽方式(推薦)
- 直接將 CSV 文件拖拽到文件列表區(qū)域
- 也可以拖拽包含 CSV 文件的文件夾
2.按鈕選擇
- 點(diǎn)擊「添加文件…」選擇單個(gè)或多個(gè) CSV 文件
- 點(diǎn)擊「添加文件夾…」選擇包含 CSV 的文件夾
3.列表管理
- 選中文件后點(diǎn)擊「移除選中」刪除不需要的文件
- 點(diǎn)擊「清空列表」清除所有文件
步驟 2:設(shè)置輸出目錄
- 在「輸出設(shè)置」區(qū)域點(diǎn)擊「瀏覽…」按鈕
- 選擇一個(gè)用于保存轉(zhuǎn)換后文件的目錄
- 建議選擇空目錄,避免同名文件被覆蓋
步驟 3:開始轉(zhuǎn)換
- 點(diǎn)擊「開始轉(zhuǎn)換」按鈕
- 觀察進(jìn)度條和狀態(tài)欄的實(shí)時(shí)反饋
- 轉(zhuǎn)換完成后會(huì)彈出結(jié)果提示框
技術(shù)實(shí)現(xiàn)
編碼檢測(cè)策略
程序采用多重編碼嘗試機(jī)制:
encodings = ['mbcs', 'ansi', 'utf-8', 'utf-8-sig']
按優(yōu)先級(jí)依次嘗試讀取,一旦成功即停止嘗試,確保最大兼容性。
文件處理流程
- 文件收集:掃描指定路徑,過(guò)濾 .csv 文件
- 編碼檢測(cè):按策略嘗試不同編碼讀取
- 內(nèi)容轉(zhuǎn)換:逐行讀取并寫入 UTF-8 文件
- 進(jìn)度反饋:實(shí)時(shí)更新界面狀態(tài)
拖拽功能實(shí)現(xiàn)
使用 tkinterdnd2 庫(kù)實(shí)現(xiàn)跨平臺(tái)拖拽支持:
self.listbox.drop_target_register(DND_FILES) self.listbox.dnd_bind('<<Drop>>', self._on_drop)
完整代碼
import csv import os import sys import tkinter as tk from tkinter import filedialog, messagebox from tkinter import ttk # 嘗試啟用拖拽支持(需要安裝: pip install tkinterdnd2) try: from tkinterdnd2 import DND_FILES, TkinterDnD # type: ignore DND_AVAILABLE = True except Exception: DND_AVAILABLE = False TkinterDnD = None # 占位,未啟用時(shí)不使用 def is_csv_file(path: str) -> bool: return os.path.isfile(path) and path.lower().endswith('.csv') def collect_csv_from_dir(folder: str): files = [] if os.path.isdir(folder): for f in os.listdir(folder): full = os.path.join(folder, f) if is_csv_file(full): files.append(full) return files def parse_dnd_event_data(root: tk.Tk, data: str): # 解析拖拽數(shù)據(jù),兼容包含空格的路徑 try: parts = root.tk.splitlist(data) except Exception: parts = data.split() return [p.strip('{').strip('}') for p in parts] class App: def __init__(self, root: tk.Tk): self.root = root self.root.title("CSV 文件轉(zhuǎn)換UTF-8格式 工具(支持拖拽/選擇文件/選擇文件夾)") self.root.geometry('760x560') self.root.minsize(680, 480) self.files = [] # 去重使用 self.files_set = set() self.output_folder_var = tk.StringVar() self._build_ui() def _build_ui(self): # ========== 輸入?yún)^(qū)域(列表 + 拖拽) ========== input_frame = ttk.LabelFrame(self.root, text="待轉(zhuǎn)換文件(將 CSV 文件或文件夾拖拽到此區(qū)域)") input_frame.pack(fill=tk.BOTH, expand=True, padx=12, pady=(12, 6)) list_frame = ttk.Frame(input_frame) list_frame.pack(fill=tk.BOTH, expand=True, padx=8, pady=8) self.listbox = tk.Listbox(list_frame, selectmode=tk.EXTENDED) self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.listbox.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.listbox.config(yscrollcommand=scrollbar.set) hint_text = "提示:可點(diǎn)擊下方按鈕添加文件/文件夾,也可直接拖拽 CSV 文件或文件夾到列表中。" self.hint_label = ttk.Label(input_frame, text=hint_text, foreground="#666") self.hint_label.pack(anchor='w', padx=10, pady=(0, 8)) # 拖拽支持 if DND_AVAILABLE and isinstance(self.root, TkinterDnD.Tk): try: self.listbox.drop_target_register(DND_FILES) self.listbox.dnd_bind('<<Drop>>', self._on_drop) except Exception: pass else: if not DND_AVAILABLE: warn = "(未安裝 tkinterdnd2,拖拽功能不可用??蓤?zhí)行: pip install tkinterdnd2)" self.hint_label.configure(text=f"{hint_text}\n{warn}") # ========== 操作按鈕 ========== btns = ttk.Frame(self.root) btns.pack(fill=tk.X, padx=12, pady=(0, 6)) ttk.Button(btns, text="添加文件…", command=self.add_files_dialog).pack(side=tk.LEFT, padx=(0, 6)) ttk.Button(btns, text="添加文件夾…", command=self.add_folder_dialog).pack(side=tk.LEFT, padx=(0, 6)) ttk.Button(btns, text="移除選中", command=self.remove_selected).pack(side=tk.LEFT, padx=(0, 6)) ttk.Button(btns, text="清空列表", command=self.clear_list).pack(side=tk.LEFT) # ========== 輸出設(shè)置 ========== out_frame = ttk.LabelFrame(self.root, text="輸出設(shè)置") out_frame.pack(fill=tk.X, padx=12, pady=6) row = ttk.Frame(out_frame) row.pack(fill=tk.X, padx=10, pady=8) ttk.Label(row, text="輸出文件夾:").pack(side=tk.LEFT) self.out_entry = ttk.Entry(row, textvariable=self.output_folder_var) self.out_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=6) ttk.Button(row, text="瀏覽…", command=self.select_output_folder).pack(side=tk.LEFT) # ========== 轉(zhuǎn)換 & 進(jìn)度 ========== bottom = ttk.Frame(self.root) bottom.pack(fill=tk.X, padx=12, pady=10) self.progress = ttk.Progressbar(bottom, mode='determinate') self.progress.pack(fill=tk.X, expand=True, side=tk.LEFT, padx=(0, 10)) ttk.Button(bottom, text="開始轉(zhuǎn)換", command=self.convert_files).pack(side=tk.RIGHT) # 狀態(tài)欄 self.status_var = tk.StringVar(value="準(zhǔn)備就緒") status = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor='w') status.pack(fill=tk.X, side=tk.BOTTOM) # ---------- 文件源管理 ---------- def _add_paths(self, paths): added = 0 for p in paths: if os.path.isdir(p): for f in collect_csv_from_dir(p): if f not in self.files_set: self.files.append(f) self.files_set.add(f) self.listbox.insert(tk.END, f) added += 1 else: if is_csv_file(p) and p not in self.files_set: self.files.append(p) self.files_set.add(p) self.listbox.insert(tk.END, p) added += 1 self.status_var.set(f"已添加 {added} 個(gè)文件,當(dāng)前總數(shù):{len(self.files)}") def _on_drop(self, event): paths = parse_dnd_event_data(self.root, event.data) self._add_paths(paths) def add_files_dialog(self): paths = filedialog.askopenfilenames(title="選擇 CSV 文件", filetypes=[("CSV 文件", "*.csv"), ("所有文件", "*.*")]) if paths: self._add_paths(paths) def add_folder_dialog(self): folder = filedialog.askdirectory(title="選擇包含 CSV 的文件夾") if folder: self._add_paths([folder]) def remove_selected(self): indices = list(self.listbox.curselection()) if not indices: return # 從后向前刪除,避免索引錯(cuò)亂 for idx in reversed(indices): path = self.listbox.get(idx) self.listbox.delete(idx) if path in self.files_set: self.files_set.remove(path) if path in self.files: self.files.remove(path) self.status_var.set(f"已移除,當(dāng)前總數(shù):{len(self.files)}") def clear_list(self): self.listbox.delete(0, tk.END) self.files.clear() self.files_set.clear() self.status_var.set("列表已清空") def select_output_folder(self): folder = filedialog.askdirectory(title="請(qǐng)選擇保存轉(zhuǎn)換后的文件夾") if folder: self.output_folder_var.set(folder) # ---------- 轉(zhuǎn)換邏輯 ---------- def _read_rows_with_encodings(self, file_path): # 按順序嘗試不同編碼讀取 encodings = ['mbcs', 'ansi', 'utf-8', 'utf-8-sig'] last_err = None for enc in encodings: try: with open(file_path, newline='', encoding=enc) as csvfile: reader = csv.reader(csvfile, delimiter=',', quotechar='"') for row in reader: yield row return except Exception as e: last_err = e continue # 如果都失敗,再拋出最后的異常 raise last_err if last_err else UnicodeDecodeError("codec", b"", 0, 1, "無(wú)法解碼") def convert_files(self): if not self.files: messagebox.showwarning("提示", "請(qǐng)先添加需要轉(zhuǎn)換的 CSV 文件或文件夾!") return output_folder = self.output_folder_var.get().strip() if not output_folder: messagebox.showerror("錯(cuò)誤", "請(qǐng)選擇保存轉(zhuǎn)換后的文件夾!") return if not os.path.exists(output_folder): try: os.makedirs(output_folder, exist_ok=True) except Exception as e: messagebox.showerror("錯(cuò)誤", f"無(wú)法創(chuàng)建輸出文件夾:{e}") return total = len(self.files) self.progress.config(maximum=total, value=0) success, failed = 0, 0 for i, src in enumerate(self.files, start=1): dst = os.path.join(output_folder, os.path.basename(src)) try: with open(dst, 'w', newline='', encoding='utf-8') as f_w: writer = csv.writer(f_w) for row in self._read_rows_with_encodings(src): writer.writerow(row) success += 1 self.status_var.set(f"轉(zhuǎn)換成功:{os.path.basename(src)}") except Exception as e: failed += 1 self.status_var.set(f"轉(zhuǎn)換失敗:{os.path.basename(src)} -> {e}") finally: self.progress['value'] = i self.root.update_idletasks() msg = f"轉(zhuǎn)換完成!成功:{success},失敗:{failed}。輸出目錄:{output_folder}" if failed: messagebox.showwarning("完成(部分失?。?, msg) else: messagebox.showinfo("完成", msg) def main(): # 優(yōu)先使用帶 DnD 的 Tk if DND_AVAILABLE: root = TkinterDnD.Tk() else: root = tk.Tk() # Windows 上 ttk 默認(rèn)樣式更美觀 try: ttk.Style().theme_use('clam') except Exception: pass app = App(root) root.mainloop() if __name__ == '__main__': main()
效果圖
常見問(wèn)題
Q: 為什么拖拽功能不可用
A: 需要安裝 tkinterdnd2 庫(kù)。運(yùn)行 pip install tkinterdnd2 后重啟程序即可。
Q: 轉(zhuǎn)換后 Excel 打開仍有亂碼
A: 本工具輸出標(biāo)準(zhǔn) UTF-8 格式。部分舊版 Excel 可能需要 UTF-8-BOM 格式,可以考慮修改輸出編碼為 utf-8-sig。
Q: 輸出文件會(huì)覆蓋原文件嗎
A: 不會(huì)。程序會(huì)在指定的輸出目錄創(chuàng)建新文件,不會(huì)修改原始文件。但如果輸出目錄中存在同名文件,會(huì)被覆蓋。
Q: 支持哪些分隔符
A: 目前默認(rèn)支持逗號(hào)分隔符。如需支持其他分隔符(如分號(hào)、制表符),可以擴(kuò)展使用 csv.Sniffer 進(jìn)行自動(dòng)檢測(cè)。
使用建議
最佳實(shí)踐
- 批量處理:將同類型的 CSV 文件放在同一文件夾,整體拖拽添加
- 輸出管理:始終選擇空的輸出目錄,避免文件覆蓋
- 編碼兼容:如需與特定軟件兼容,可調(diào)整輸出編碼格式
性能優(yōu)化
- 大文件處理時(shí),程序會(huì)顯示實(shí)時(shí)進(jìn)度
- 支持中斷操作(關(guān)閉程序窗口)
- 內(nèi)存占用優(yōu)化,逐行處理避免大文件內(nèi)存溢出
擴(kuò)展功能規(guī)劃
- 輸出文件名自動(dòng)添加后綴,避免覆蓋
- 自動(dòng)分隔符檢測(cè)與配置
- 轉(zhuǎn)換報(bào)告導(dǎo)出(成功/失敗統(tǒng)計(jì))
- 遞歸掃描子目錄選項(xiàng)
- 編碼檢測(cè)結(jié)果預(yù)覽
- 批量重命名功能
開發(fā)者: 專注于提供簡(jiǎn)單可靠的數(shù)據(jù)處理工具
到此這篇關(guān)于Python實(shí)現(xiàn)CSV文件編碼轉(zhuǎn)換工具(轉(zhuǎn)成UTF-8格式)的文章就介紹到這了,更多相關(guān)Python轉(zhuǎn)換csv文件編碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用PyInstaller將Python程序文件轉(zhuǎn)換為可執(zhí)行程序文件
與py2exe一樣,PyInstaller程序也可以將Python的.py程序文件轉(zhuǎn)換為.exe,并且還有Linux的版本,下面我們就來(lái)詳細(xì)看一下如何使用PyInstaller將Python程序文件轉(zhuǎn)換為可執(zhí)行程序文件2016-07-07python爬取晉江文學(xué)城小說(shuō)評(píng)論(情緒分析)
這篇文章主要介紹了使用python爬取晉江文學(xué)城小說(shuō)評(píng)論(情緒分析),全文代碼詳細(xì),邏輯清晰,很適合學(xué)習(xí)爬蟲爬取的朋友,需要的朋友可以參考下2021-04-04scrapy實(shí)踐之翻頁(yè)爬取的實(shí)現(xiàn)
這篇文章主要介紹了scrapy實(shí)踐之翻頁(yè)爬取的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01把csv文件轉(zhuǎn)化為數(shù)組及數(shù)組的切片方法
今天小編就為大家分享一篇把csv文件轉(zhuǎn)化為數(shù)組及數(shù)組的切片方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07python基礎(chǔ)詳解之if循環(huán)語(yǔ)句
這篇文章主要介紹了python基礎(chǔ)詳解之if循環(huán)語(yǔ)句,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)python的小伙伴們有很好的幫助需要的朋友可以參考下2021-04-04Python調(diào)用C/C++函數(shù)庫(kù)的多種方法與實(shí)踐指南
Python作為一門高級(jí)編程語(yǔ)言,以其簡(jiǎn)潔的語(yǔ)法和豐富的庫(kù)生態(tài)贏得了開發(fā)者的青睞,然而,在計(jì)算密集型任務(wù)中,Python的性能往往無(wú)法滿足要求,Python調(diào)用C/C++函數(shù)庫(kù)成為提升應(yīng)用性能的關(guān)鍵技術(shù)路徑,本文將深入探討Python調(diào)用C/C++函數(shù)庫(kù)的多種方法,需要的朋友可以參考下2025-08-08