[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[问题求助] 【己解决】想用python为文本添加点东西,可速度太慢,求助更快的算法

本帖最后由 batsealine 于 2016-4-4 10:40 编辑

先举个例子展示脚本要干的事:
有两个文件 jd.txtstem.txt  jd.txt 中有“苏   eyo”, stem.txt中有"苏   ey",现要生成一个新文件里面为"苏  eyo ey",再具体一点:
jd.txt 是这样的:
木桨        fatr
本应        fatv
枰        faua
苏        eyo

stem.txt 是这样的(全部是单字):
苏        ey
枰        fa

要求生成的 new_jd.txt :
木桨        fatr
本应        fatv
枰        faua        fa
苏        eyo        ey

要求 new_jd.txt 中字的顺序不变,相当于只在 jd.txt 中部份字后加入一列构词码

文件下载地址(右键另存为):jd.txt        stem.txt


我已经用for循环实现了,两个文件分别有18万行和3万行,这样下来需要15分钟左右,现在求一个更好的方法,如果有谁会 grep awk,也欢迎指点。

下面是我的代码:
  1. # -*- encoding: UTF-8 -*-
  2. # 此脚本的作用是将stem.txt中的构词码加到指定的码表中,stem.txt从https://github.com/lotem/rime-forge/tree/master/zhengma处得来(在dict.ymal的最下面)
  3. # usage:add_stem.py filename/filepath
  4. # 它将在当前文件夹生成一个新文件,命名为 new_*
  5. import re
  6. import os
  7. import sys
  8. import codecs
  9. # 用codecs以指定编码打开文件很有效,不然有时会有错误
  10. filepath = sys.argv[1]
  11. filename = re.search(r'(?:.*/)?(.*)\.', filepath).group(1)               #[/\\]用来兼容\路径和/路径, (?:..)?用来兼容没有路径只有文件名的情况
  12. newFilename = "new_" + filename + ".txt"
  13. f = codecs.open(filepath, "r", 'utf-8')
  14. lines = f.readlines()           #readlines()能生成一个list
  15. f.close()
  16. f_stem = codecs.open(r'stem.txt', "r", 'utf-8')
  17. stemlines = f_stem.readlines()
  18. f_stem.close()
  19. newf = codecs.open(newFilename, "w", 'utf-8')
  20. for i,line in enumerate(lines):
  21. # enumerate这个方法是从网上找的可以方便的找出循环的次数
  22.     words = line.split()
  23.     myre = words[0]
  24.     if (len(myre) == 1):   # 只为单字查找构词码
  25.         for stemline in stemlines:
  26.             if (re.match(myre, stemline)):   # 汉字可以直接用正则匹配
  27.                 line = line.strip('\r\n') + "\t" + stemline.split()[1] + '\n'    # 用line.strip('\r\n')去除行尾的换行符,注意一定不要忘了'\r'
  28.                 break
  29.     newf.write(line)
  30.     if (i % 100 == 0):
  31.         print(i)
  32. # 这里打印出行数是为了查看进度,因为整个过程大概要15分钟,当行数为18万时都差不多完了。
  33. newf.close()
复制代码
############################## 分割线

用了版主的 OrderedDict 后,程序的运行时间只要1.1s,非常满意
  1. # -*- encoding: UTF-8 -*-
  2. # 此脚本的作用是将stem.txt中的构词码加到指定的码表中,stem.txt从https://github.com/lotem/rime-forge/tree/master/zhengma处得来(在dict.ymal的最下面)
  3. # usage:add_stem.py filename/filepath
  4. # 它将在当前文件夹生成一个新文件,命名为 new_filename
  5. import re
  6. import os
  7. import sys
  8. import codecs
  9. from time import time
  10. from collections import OrderedDict    # 待找相关资料,这是加快的速度的核心
  11. # 用codecs以指定编码打开文件很有效,不然有时会有错误
  12. filepath = sys.argv[1]
  13. filename = os.path.basename(filepath)
  14. newFilename = "new_" + filename
  15. f = codecs.open(filepath, "r", 'utf-8')
  16. lines = f.readlines()           #readlines()能生成一个list
  17. f.close()
  18. f_stem = codecs.open(r'stem.txt', "r", 'utf-8')
  19. stemlines = f_stem.readlines()
  20. f_stem.close()
  21. newf = codecs.open(newFilename, "w", 'utf-8')
  22. # 先将stem.txt 的内容组成二元列表
  23. stemwords = []
  24. for stemline in stemlines:
  25.     stemline_split = stemline.split()
  26.     stemwords.append((stemline_split[0], stemline_split[1]))
  27. # 再stemwords这个二元列表变为有序的字典,具体的原理还不清楚
  28. stem = OrderedDict(stemwords)
  29. for i,line in enumerate(lines):
  30. # enumerate这个方法是从网上找的,可以方便的找出循环的次数
  31.     word = line.split()[0]
  32.     if (len(word) == 1 and stem.get(word)):   
  33.     # stem.get(word) 判断word在stem中是否存在,stem[word]找出word在stem.txt所对应的构词码
  34.             line = line.strip('\r\n') + '\t' + stem[word] + '\n'
  35.     newf.write(line)
  36. newf.close()
复制代码

回复 1# batsealine
你这文件全是utf8特殊字符,不知linux下的sort能处理不?我觉得应该将jd.txt和stem.txt合为一个txt。然后用sort排序,变相同首字紧排。之后挨行匹配替换会快非常多

TOP

回复 2# happy886rr


    这是词库文件,顺序不能变的,不知道多线程是不适用这种情况。

TOP

本帖最后由 pcl_test 于 2016-4-3 21:17 编辑

举个栗子,字典法
  1. # encoding: UTF-8
  2. from collections import OrderedDict
  3. stem = OrderedDict([('a','1'), ('b','2'), ('c','3')])
  4. jd = ['a 2', 'a 3', 'c 1', 'd 4', 'e 5', 'b 6']
  5. for i in jd:
  6. t = i.split()
  7. if stem.get(t[0]):
  8. stem[t[0]] += '\t' + t[1]
  9. for i, j in stem.items():
  10. print(i+'\t'+j)
复制代码
3

评分人数

    • CrLf: 666技术 + 1
    • batsealine: 这样实在太快了技术 + 1
    • codegay: 天啊,版主还能写得一手python技术 + 1

TOP

  1. os.path.basename(path)
复制代码
这样取文件名好了.
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

本帖最后由 happy886rr 于 2016-4-3 19:45 编辑

回复 3# batsealine
花费15分钟去处理20万行,确实太漫长。算法的问题。加上序号,先从每行序号之后sort排序处理替换完,再按序号排恢复,最后替掉序号。

TOP

处理规则还得说清楚一点才好.
不然还得自己看代码确认你说的规则是什么样的.
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

  1. stem.txt的编码没问题{'confidence': 1.0, 'encoding': 'UTF-8-SIG'}
  2. jd.txt的编码,我这里解不了.
  3. {'confidence': 0.20146231414170868, 'encoding': 'ISO-8859-2'}
复制代码
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xed in position 851491: invalid continuation byte
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

本帖最后由 codegay 于 2016-4-3 21:18 编辑

回复 6# happy886rr


    他用jd.txt18万行去迭代,stem.txt3万行.

迭代数量相当于
18万X30000?

处理量级比较大的东西不要用print来打印进度.很影响速度.
python有很多进度显示的库.我看到过,名字我忘记了.
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

本帖最后由 happy886rr 于 2016-4-3 21:19 编辑

耗时15分钟。平均每秒才生成不到3KB文件,都成了写日志。意思是总共进行了54亿次比较,天文数字。
我发现他追加的那3万行都是单字,所以遇到非单字的行直接跳过,没必要追加。这样最多比较9亿次,耗时2分半。

TOP

回复 4# pcl_test


   完美解决,只用1.1s

TOP

回复 5# codegay


    多谢提醒,再不改就要被笑话了。
还有那个print的问题,那也只是权宜之计,等以后真正需要进度条再去网上找吧。

TOP

回复 12# batsealine


    并没有.
只是我懒.不爱写正则.

另外basename取出来的是带后缀的.所以也不见得是很好的方法.
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

回复 8# codegay


    不太清楚你是怎么做的,不过你确定是用 utf-8 来 encode 的吗

TOP

回复 14# batsealine


    肯定是的.
没事.这个我先不纠结了.这编码问题的坑以后有的是机会踩.
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

返回列表