批处理之家's Archiver

batsealine 发表于 2016-4-3 16:18

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

[i=s] 本帖最后由 batsealine 于 2016-4-4 10:40 编辑 [/i]

先举个例子展示脚本要干的事:
有两个文件 [b]jd.txt[/b] 和 [b]stem.txt  [/b]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 中部份字后加入一列构词码

文件下载地址(右键另存为):[url=http://sealine.oss-cn-qingdao.aliyuncs.com/refer/jd.txt][b]jd.txt[/b][/url]        [url=http://sealine.oss-cn-qingdao.aliyuncs.com/refer/stem.txt][b]stem.txt[/b][/url]


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

下面是我的代码:[code]# -*- encoding: UTF-8 -*-
# 此脚本的作用是将stem.txt中的构词码加到指定的码表中,stem.txt从https://github.com/lotem/rime-forge/tree/master/zhengma处得来(在dict.ymal的最下面)
# usage:add_stem.py filename/filepath
# 它将在当前文件夹生成一个新文件,命名为 new_*

import re
import os
import sys
import codecs
# 用codecs以指定编码打开文件很有效,不然有时会有错误
filepath = sys.argv[1]
filename = re.search(r'(?:.*/)?(.*)\.', filepath).group(1)               #[/\\]用来兼容\路径和/路径, (?:..)?用来兼容没有路径只有文件名的情况
newFilename = "new_" + filename + ".txt"
f = codecs.open(filepath, "r", 'utf-8')
lines = f.readlines()           #readlines()能生成一个list
f.close()

f_stem = codecs.open(r'stem.txt', "r", 'utf-8')
stemlines = f_stem.readlines()
f_stem.close()

newf = codecs.open(newFilename, "w", 'utf-8')

for i,line in enumerate(lines):
# enumerate这个方法是从网上找的可以方便的找出循环的次数
    words = line.split()
    myre = words[0]
    if (len(myre) == 1):   # 只为单字查找构词码
        for stemline in stemlines:
            if (re.match(myre, stemline)):   # 汉字可以直接用正则匹配
                line = line.strip('\r\n') + "\t" + stemline.split()[1] + '\n'    # 用line.strip('\r\n')去除行尾的换行符,注意一定不要忘了'\r'
                break
    newf.write(line)
    if (i % 100 == 0):
        print(i)
# 这里打印出行数是为了查看进度,因为整个过程大概要15分钟,当行数为18万时都差不多完了。
newf.close()[/code]############################## 分割线

用了版主的 [b]OrderedDict[/b] 后,程序的运行时间只要1.1s,非常满意[code]# -*- encoding: UTF-8 -*-
# 此脚本的作用是将stem.txt中的构词码加到指定的码表中,stem.txt从https://github.com/lotem/rime-forge/tree/master/zhengma处得来(在dict.ymal的最下面)
# usage:add_stem.py filename/filepath
# 它将在当前文件夹生成一个新文件,命名为 new_filename

import re
import os
import sys
import codecs
from time import time
from collections import OrderedDict    # 待找相关资料,这是加快的速度的核心

# 用codecs以指定编码打开文件很有效,不然有时会有错误
filepath = sys.argv[1]
filename = os.path.basename(filepath)
newFilename = "new_" + filename
f = codecs.open(filepath, "r", 'utf-8')
lines = f.readlines()           #readlines()能生成一个list
f.close()

f_stem = codecs.open(r'stem.txt', "r", 'utf-8')
stemlines = f_stem.readlines()
f_stem.close()

newf = codecs.open(newFilename, "w", 'utf-8')


# 先将stem.txt 的内容组成二元列表
stemwords = []
for stemline in stemlines:
    stemline_split = stemline.split()
    stemwords.append((stemline_split[0], stemline_split[1]))

# 再stemwords这个二元列表变为有序的字典,具体的原理还不清楚
stem = OrderedDict(stemwords)


for i,line in enumerate(lines):
# enumerate这个方法是从网上找的,可以方便的找出循环的次数
    word = line.split()[0]
    if (len(word) == 1 and stem.get(word)):   
    # stem.get(word) 判断word在stem中是否存在,stem[word]找出word在stem.txt所对应的构词码
            line = line.strip('\r\n') + '\t' + stem[word] + '\n'
    newf.write(line)

newf.close()[/code]

happy886rr 发表于 2016-4-3 18:01

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=184370&ptid=39926]1#[/url] [i]batsealine[/i] [/b]
你这文件全是utf8特殊字符,不知linux下的sort能处理不?我觉得应该将jd.txt和stem.txt合为一个txt。然后用sort排序,变相同首字紧排。之后挨行匹配替换会快非常多

batsealine 发表于 2016-4-3 19:12

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=184372&ptid=39926]2#[/url] [i]happy886rr[/i] [/b]


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

pcl_test 发表于 2016-4-3 19:24

[i=s] 本帖最后由 pcl_test 于 2016-4-3 21:17 编辑 [/i]

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

codegay 发表于 2016-4-3 19:30

[code]os.path.basename(path)[/code]这样取文件名好了.

happy886rr 发表于 2016-4-3 19:33

[i=s] 本帖最后由 happy886rr 于 2016-4-3 19:45 编辑 [/i]

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=184373&ptid=39926]3#[/url] [i]batsealine[/i] [/b]
花费15分钟去处理20万行,确实太漫长。算法的问题。加上序号,先从每行序号之后sort排序处理替换完,再按序号排恢复,最后替掉序号。

codegay 发表于 2016-4-3 19:50

处理规则还得说清楚一点才好.
不然还得自己看代码确认你说的规则是什么样的.

codegay 发表于 2016-4-3 20:35

[code]stem.txt的编码没问题{'confidence': 1.0, 'encoding': 'UTF-8-SIG'}

jd.txt的编码,我这里解不了.
{'confidence': 0.20146231414170868, 'encoding': 'ISO-8859-2'}[/code]UnicodeDecodeError: 'utf-8' codec can't decode byte 0xed in position 851491: invalid continuation byte

codegay 发表于 2016-4-3 20:49

[i=s] 本帖最后由 codegay 于 2016-4-3 21:18 编辑 [/i]

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=184377&ptid=39926]6#[/url] [i]happy886rr[/i] [/b]


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

迭代数量相当于
18万X30000?

处理量级比较大的东西不要用print来打印进度.很影响速度.
python有很多进度显示的库.我看到过,名字我忘记了.

happy886rr 发表于 2016-4-3 21:07

[i=s] 本帖最后由 happy886rr 于 2016-4-3 21:19 编辑 [/i]

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

batsealine 发表于 2016-4-4 09:52

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=184375&ptid=39926]4#[/url] [i]pcl_test[/i] [/b]


[size=5]   完美解决,只用[color=Red]1.1s[/color][/size]

batsealine 发表于 2016-4-4 09:59

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=184376&ptid=39926]5#[/url] [i]codegay[/i] [/b]


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

codegay 发表于 2016-4-4 10:22

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=184400&ptid=39926]12#[/url] [i]batsealine[/i] [/b]


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

另外basename取出来的是带后缀的.所以也不见得是很好的方法.

batsealine 发表于 2016-4-4 10:38

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=184384&ptid=39926]8#[/url] [i]codegay[/i] [/b]


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

codegay 发表于 2016-4-4 10:56

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=184402&ptid=39926]14#[/url] [i]batsealine[/i] [/b]


    肯定是的.
没事.这个我先不纠结了.这编码问题的坑以后有的是机会踩.

949825667@qq.co 发表于 2016-9-2 19:16

[i=s] 本帖最后由 949825667@qq.co 于 2016-9-2 21:34 编辑 [/i]

无意中看到的,不知道算不算挖坟!
算是一种思路吧!效率还不错![code]open($IN,'<jd.txt');
open($IN1,'<stem.txt');
open($OUT,'>out.txt');
while(<$IN1>){
  ($C,$D)=split(/\s+/,$_);
   $dic1{$C}=$D;
}
while(<$IN>){
  ($A,$B)=split(/\s+/,$_);
if(exists $dic1{$A}){
        print $OUT "$A $B $dic1{$A}\n";
        }else{
  print $OUT "$A $B\n";
}}
[/code]

523066680 发表于 2016-9-2 20:15

[i=s] 本帖最后由 523066680 于 2016-9-2 20:59 编辑 [/i]

没有缩进,差评!

也写了一个
[font=consolas][size=15px][list=1][*]open [color=#007800]$RA[/color], [color=#ff0000]"<:raw"[/color], [color=#ff0000]"jd.txt"[/color];
[*]open [color=#007800]$RB[/color], [color=#ff0000]"<:raw"[/color], [color=#ff0000]"stem.txt"[/color];
[*]open [color=#007800]$WRT[/color], [color=#ff0000]">:raw"[/color], [color=#ff0000]"out_jd.txt"[/color];
[*]
[*]my [color=#007800]$line[/color];
[*]my [color=#000000][b]%[/b][/color][color=#7a0874][b]hash[/b][/color];
[*][color=#000000][b]while[/b][/color] [color=#7a0874][b]([/b][/color][color=#007800]$line[/color] = [color=#000000][b]<[/b][/color][color=#007800]$RB[/color][color=#000000][b]>[/b][/color][color=#7a0874][b])[/b][/color]
[*][color=#7a0874][b]{[/b][/color]
[*]    [color=#007800]$line[/color] =~ [color=#000000][b]/[/b][/color][color=#7a0874][b]([/b][/color].[color=#000000][b]*[/b][/color]?[color=#7a0874][b])[/b][/color]\s+[color=#7a0874][b]([/b][/color]\[color=#c20cb9][b]w[/b][/color]+[color=#7a0874][b])[/b][/color][color=#000000][b]/[/b][/color];
[*]    [color=#007800]$hash[/color][color=#7a0874][b]{[/b][/color][color=#007800]$1[/color][color=#7a0874][b]}[/b][/color] = [color=#007800]$2[/color];
[*][color=#7a0874][b]}[/b][/color]
[*]
[*][color=#000000][b]while[/b][/color] [color=#7a0874][b]([/b][/color][color=#007800]$line[/color] = [color=#000000][b]<[/b][/color][color=#007800]$RA[/color][color=#000000][b]>[/b][/color][color=#7a0874][b])[/b][/color]
[*][color=#7a0874][b]{[/b][/color]
[*]    [color=#007800]$line[/color] =~ s[color=#000000][b]/[/b][/color]\r?\n$[color=#000000][b]//[/b][/color];
[*]    [color=#007800]$line[/color] =~ [color=#000000][b]/[/b][/color]^[color=#7a0874][b]([/b][/color].[color=#000000][b]*[/b][/color]?[color=#7a0874][b])[/b][/color]\s+[color=#000000][b]/[/b][/color];
[*]    print [color=#007800]$WRT[/color] [color=#007800]$line[/color];
[*]    print [color=#007800]$WRT[/color] [color=#ff0000]"[color=#000099][b]\t[/b][/color]"[/color].[color=#007800]$hash[/color][color=#7a0874][b]{[/b][/color][color=#007800]$1[/color][color=#7a0874][b]}[/b][/color] [color=#000000][b]if[/b][/color] [color=#7a0874][b]([/b][/color]exists [color=#007800]$hash[/color][color=#7a0874][b]{[/b][/color][color=#007800]$1[/color][color=#7a0874][b]}[/b][/color][color=#7a0874][b])[/b][/color];
[*]    print [color=#007800]$WRT[/color] [color=#ff0000]"[color=#000099][b]\r[/b][/color][color=#000099][b]\n[/b][/color]"[/color];
[*][color=#7a0874][b]}[/b][/color]
[*]
[*]close [color=#007800]$RA[/color];
[*]close [color=#007800]$RB[/color];
[*]close [color=#007800]$WRT[/color];
[/list][/size][/font]

949825667@qq.co 发表于 2016-9-2 20:32

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=190035&ptid=39926]17#[/url] [i]523066680[/i] [/b]


    尽量养成好习惯!python就不用担心缩进问题!不缩进会提醒!:L

523066680 发表于 2016-9-2 21:03

[i=s] 本帖最后由 523066680 于 2016-9-2 21:04 编辑 [/i]

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=190037&ptid=39926]18#[/url] [i]949825667@qq.co[/i] [/b]


    你的输出结果不对,后面的信息没追加成功。
例如[code]挻        dmiy        dy[/code]

949825667@qq.co 发表于 2016-9-2 21:10

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=190038&ptid=39926]19#[/url] [i]523066680[/i] [/b]


    我先删除代码,再检查一下

949825667@qq.co 发表于 2016-9-2 21:35

[i=s] 本帖最后由 949825667@qq.co 于 2016-9-2 21:36 编辑 [/i]

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=190040&ptid=39926]20#[/url] [i]949825667@qq.co[/i] [/b]


    改了一下,可以了,和你的结果比对了一下!没啥问题了!

kongfuzhou 发表于 2018-5-21 15:39

[i=s] 本帖最后由 kongfuzhou 于 2018-5-21 15:43 编辑 [/i]

先定义一个list把所有要写入txt的文本放进去,然后把list字符串链接成一个字符串,最后写入文件。速度还可以快5、6倍,本地测试300ms左右

例如:[code]words = []
words.append(lineStr)
txt = "".join(words)[/code]

页: [1]

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.