บันทึกโค๊ดที่ทดลองเขียน
หาค่า factor ของ 2 จำนวนเต็ม
สมการคือ (nX * x) + (nY * y) = z
รู้ x, y, และ z
อยากทราบจำนวน nX และ nY ที่เป็นจำนวนเต็ม ว่ามีค่าเท่าไหร่มั่ง
ตั้งชื่อโปรแกรมว่า d.calc2.py
$ vi d.calc2.py
#!/usr/bin/env python
import sys
if sys.argv[3]==None:
nSum=input("Enter sum amount: ")
nXparms = input("Enter x parms: ")
nYparms = input("Enter y parms: ")
else:
nSum=eval(sys.argv[1])
nXparms=eval(sys.argv[2])
nYparms=eval(sys.argv[3])
#
nX=0
nY=1
print "nX*%f + nY*%f = %f" % (nXparms, nYparms, nSum)
while nY>0:
nY=float(nSum-nXparms*nX)/nYparms
nRemain=float(nY-int(nY))
if nRemain==0:
print "nX=%i, nY=%i" % (nX,nY)
#
nX=nX+1
#
ทดลองเรียกใช้งาน
$ ./d.calc2.py 50 1.5 2.5
nX*1.500000 + nY*2.500000 = 50.000000
nX=0, nY=20
nX=5, nY=17
nX=10, nY=14
nX=15, nY=11
nX=20, nY=8
nX=25, nY=5
nX=30, nY=2
วันนี้มีงานต้องแปลงไฟล์ จาก tis620 ไป utf8
คำสั่งที่ต้องใช้คือ
$ iconv -f tis620 -t utf8 -o NEWFILE.TXT OLDFILE.TXT
แต่ปัญหาคือหลายไฟล์แปลงไม่ได้ เขาไม่ยอมแปลงให้
อาจเป็นเพราะมีรหัสแปลกปลอมในไฟล์
เลยเขียนสคริปต์ไว้ใช้แปลงเอง จะได้เผื่อสำหรับงานหน้าด้วย
ตั้งชื่อว่า d.tis2utf เอาไว้ใน /usr/local/bin
$ sudo touch /usr/local/bin/d.tis2utf
$ sudo chmod 755 /usr/local/bin/d.tis2utf
$ sudo vi /usr/local/bin/d.tis2utf
#!/usr/bin/env python
# CONVERT FILE CONTENT FROM tis620(cp874) TO utf8
import sys,os
# GLOBAL VARS
decodec="cp874"
encodec="utf8"
#
# VARIABLE decodec AND encodec CAN BE CHANGED.
# POSSIBLE STANDARD ENCODINGS VALUES ARE:
# ascii, big5, big5hkscs, cp037, cp424, cp437, cp500, cp737, cp775, cp850,
# cp852, cp855, cp856, cp857, cp860, cp861, cp862, cp863, cp864, cp865,
# cp866, cp869, cp874, cp875, cp932, cp949, cp950, cp1006, cp1026, cp1140,
# cp1250, cp1251, cp1252, cp1253, cp1254, cp1255, cp1256, cp1257, cp1258,
# euc_jp, euc_jis_2004, euc_jisx0213, euc_kr, gb2312, gbk, gb18030, hz,
# iso2022_jp, iso2022_jp_1, iso2022_jp_2, iso2022_jp_2004, iso2022_jp_3,
# iso2022_jp_ext, iso2022_kr, latin_1, iso8859_2, iso8859_3, iso8859_4,
# iso8859_5, iso8859_6, iso8859_7, iso8859_8, iso8859_9, iso8859_10,
# iso8859_13, iso8859_14, iso8859_15, johab, koi8_r, koi8_u, mac_cyrillic,
# mac_greek, mac_iceland, mac_latin2, mac_roman, mac_turkish, ptcp154,
# shift_jis, shift_jis_2004, shift_jisx0213, utf_16, utf_16_be, utf_16_le,
# utf_7, utf_8, utf_8_sig
#
# PLEASE SEE http://docs.python.org/lib/standard-encodings.html
# FOR MORE INFORMATION.
def usage(progname):
print "Usage: %s FILE" % (progname)
print "Convert FILE from %s to %s, save old file in FILE.bak" % (decodec,encodec)
def cannotopenfile(filename):
print "Cannot open file %s" % (filename)
def genfilename(filename="",ext="new"):
if filename=="":
return ""
#
if ext.lower()=="new":
ext="new"
#
if ext.lower()!="new" and ext.lower()!="bak":
ext="bak"
#
if os.path.exists(filename+"."+ext):
i=0
while os.path.exists(filename+"."+ext+str(i)) and (i < 1000):
i=i+1
#
if i>999:
return ""
#
return filename+"."+ext+str(i)
else:
return filename+"."+ext
#
def convertfile(fs_old, fs_new):
for eachline in fs_old:
try:
newline=eachline.decode(decodec).encode(encodec)
except:
newline=eachline
#
fs_new.write(newline)
#
return True
if __name__=="__main__":
progname=os.path.basename(sys.argv[0])
try:
oldfile=sys.argv[1]
except:
usage(progname)
sys.exit(1)
#
try:
fsold=open(oldfile)
except:
cannotopenfile(oldfile)
sys.exit(1)
#
newfile=genfilename(oldfile,"new")
if newfile=="":
print "Cannot save backup file"
sys.exit(1)
#
try:
fsnew=open(newfile,"w")
except:
cannotopenfile(newfile)
sys.exit(1)
#
if convertfile(fsold,fsnew)==False:
fsold.close()
fsnew.close()
print "Convert file %s faild" % (oldfile)
sys.exit(1)
#
fsold.close()
fsnew.close()
bakfile=genfilename(oldfile,"bak")
if bakfile=="":
print "Cannot create bakup file, so utf8-file is %s" % (newfile)
sys.exit(1)
#
os.rename(oldfile,bakfile)
os.rename(newfile,oldfile)
print "Convert %s success, save backup file in %s" % (oldfile,bakfile)
วิธีใช้ก็สั่ง d.tis2utf FILENAME.TXT
จะได้ FILENAME.TXT เป็นรหัส utf8 และ FILENAME.TXT.bak เป็นไฟล์เก่า
(ใครเขียนไพธอนเก่ง ๆ ฝากแนะนำด้วยครับ อยากเขียนให้กระชับ และดูง่าย)
มีงานต้องแปลงไฟล์กลับ เลยเขียนโค๊ดอีกทีนึง
( งานที่ทำคือ สันติรำลึก เป็นการแปลงไฟล์กลับจาก Word มาเป็น HTML แบบ tis-620 )
#!/usr/bin/env python
# CONVERT FILE CONTENT FROM utf8 TO tis620
import sys,os
# GLOBAL VARS
decodec="utf8"
encodec="cp874"
#
# VARIABLE decodec AND encodec CAN BE CHANGED.
# ALL STANDARD ENCODINGS IS:
# ascii, big5, big5hkscs, cp037, cp424, cp437, cp500, cp737, cp775, cp850,
# cp852, cp855, cp856, cp857, cp860, cp861, cp862, cp863, cp864, cp865,
# cp866, cp869, cp874, cp875, cp932, cp949, cp950, cp1006, cp1026, cp1140,
# cp1250, cp1251, cp1252, cp1253, cp1254, cp1255, cp1256, cp1257, cp1258,
# euc_jp, euc_jis_2004, euc_jisx0213, euc_kr, gb2312, gbk, gb18030, hz,
# iso2022_jp, iso2022_jp_1, iso2022_jp_2, iso2022_jp_2004, iso2022_jp_3,
# iso2022_jp_ext, iso2022_kr, latin_1, iso8859_2, iso8859_3, iso8859_4,
# iso8859_5, iso8859_6, iso8859_7, iso8859_8, iso8859_9, iso8859_10,
# iso8859_13, iso8859_14, iso8859_15, johab, koi8_r, koi8_u, mac_cyrillic,
# mac_greek, mac_iceland, mac_latin2, mac_roman, mac_turkish, ptcp154,
# shift_jis, shift_jis_2004, shift_jisx0213, utf_16, utf_16_be, utf_16_le,
# utf_7, utf_8, utf_8_sig
#
# SEE http://docs.python.org/lib/standard-encodings.html
# FOR MORE INFORMATION.
def usage(progname):
print "Usage: %s FILE" % (progname)
print "Convert FILE from %s to %s, save old file in FILE.bak" % (decodec,encodec)
def cannotopenfile(filename):
print "Cannot open file %s" % (filename)
def genfilename(filename="",ext="new"):
if filename=="":
return ""
#
if ext.lower()=="new":
ext="new"
#
if ext.lower()!="new" and ext.lower()!="bak":
ext="bak"
#
if os.path.exists(filename+"."+ext):
i=0
while os.path.exists(filename+"."+ext+str(i)) and (i < 1000):
i=i+1
#
if i>999:
return ""
#
return filename+"."+ext+str(i)
else:
return filename+"."+ext
#
def replace_invalid_char(line,utf_char,tis_char):
return line.replace(utf_char,tis_char)
def convertline(line):
# CHECK INVALID CHAR
line=replace_invalid_char(line,"\xe2\x80\x98","'")
line=replace_invalid_char(line,"\xe2\x80\x99","'")
line=replace_invalid_char(line,"\xe2\x80\x9c",'"')
line=replace_invalid_char(line,"\xe2\x80\x9d",'"')
line=replace_invalid_char(line,"\xe2\x80\xa6","...")
line=replace_invalid_char(line,"\xef\x9c\x8f","\xe0\xb8\x8d") #YOR YING
line=replace_invalid_char(line,"\xef\x9c\x9a","\xe0\xb8\xba") #PINTU
line=replace_invalid_char(line,"\xe2\x80\x93","-")
line=replace_invalid_char(line,"\xef\x82\xae","->")
line=replace_invalid_char(line,"\xef\xa3\x82","") # UNKNOWN
line=replace_invalid_char(line,"\xef\xa3\x83","") # UNKNOWN
return line.decode(decodec).encode(encodec)
def convertfile(fs_old, fs_new):
for eachline in fs_old:
newline=convertline(eachline)
# try:
# newline=convertline(eachline)
# except:
# newline=eachline
# #
fs_new.write(newline)
#
return True
if __name__=="__main__":
progname=os.path.basename(sys.argv[0])
try:
oldfile=sys.argv[1]
except:
usage(progname)
sys.exit(1)
#
try:
fsold=open(oldfile)
except:
cannotopenfile(oldfile)
sys.exit(1)
#
newfile=genfilename(oldfile,"new")
if newfile=="":
print "Cannot save backup file"
sys.exit(1)
#
try:
fsnew=open(newfile,"w")
except:
cannotopenfile(newfile)
sys.exit(1)
#
if convertfile(fsold,fsnew)==False:
fsold.close()
fsnew.close()
print "Convert file %s faild" % (oldfile)
sys.exit(1)
#
fsold.close()
fsnew.close()
bakfile=genfilename(oldfile,"bak")
if bakfile=="":
print "Cannot create bakup file, so utf8-file is %s" % (newfile)
sys.exit(1)
#
os.rename(oldfile,bakfile)
os.rename(newfile,oldfile)
print "Convert %s success, save backup file in %s" % (oldfile,bakfile)
โค๊ดยังไม่เรียบร้อยดี แต่ขอแปะโค๊ดไว้ก่อน
จาก ThaiLinuxCafe: แก้ไข ID3Tags ใน mp3 ให้ใช้กับ Amarok 1.4 และ Noatun
ใช้แปลงไฟล์ mp3 จากการเข้ารหัสแบบ cp874 มาเป็นยูนิโค๊ด utf8
ตัวโปรแกรมจะแปลงชื่อไฟล์และ ID3 Tags ในไฟล์
ถ้าจะนำไปใช้ โปรดใช้ด้วยความระมัดระวัง
เพราะไม่ได้เขียนฟังก์ชั่นการเตือนไว้ด้วยครับ
ตั้งชื่อไฟล์ว่า d.tags2utf8
$ sudo touch /usr/local/bin/d.tags2utf8
$ sudo chmod 755 /usr/local/bin/d.tags2utf8
$ sudo vi /usr/local/bin/d.tags2utf8
#!/usr/bin/env python
"""
Convert ID3 Tags from CP874 to UTF8 and auto rename file
recursive into subdirectory.
Coding from Khun pong_th's article at:
http://www.thailinuxhosting.com/yabbse/index.php?board=6;action=display;threadid=9429
"""
# 49-11-18 ADD ID3V1 CONVERSION
import os
# GLOBAL VARIABLE
skel=".mp3"
decodec="cp874"
encodec="utf8"
def d_passcheck_invalid_char(string):
if string=="":
return False
i=0
for ch in ['\x00','\xff']:
i=i+1
if ch in string:
print "%d CH IN STRING %s" % (i,string)
return False
#
#
return True
def d_convert(string):
string=string.split('\00')[0] # TRIM '\x00' CHARACTER
if d_passcheck_invalid_char(string):
return string.decode(decodec).encode(encodec)
else:
return string
#
def d_rename2utf8(dir,strname):
"""Convert coding from TIS-620 to UTF-8."""
for i in strname:
if i>'\x7f':
# CHECK FOR UTF STRING
if i=='\xe0':
return strname
newstr=d_convert(strname)
print "rename file: %s -> %s" % (strname,newstr)
os.rename(dir+os.sep+strname, dir+os.sep+newstr)
return newstr
#
#
return strname
def d_getID3V1data(fstream):
"""Get old tags format
From: http://www.faqs.org/docs/diveintopython/fileinfo_files.html"""
# ID3V1 format (from:http://www.id3.org/id3v2-00.txt)
# Field Length Offsets
# Tag 3 0-2
# Songname 30 3-32
# Artist 30 33-62
# Album 30 63-92
# Year 4 93-96
# Comment 30 97-126
# Genre 1 127
fstream.seek(-128,2)
tags=fstream.read(128)
fstream.seek(0)
return [tags[3:32],tags[33:62],tags[63:92],tags[93:96],tags[97:126],tags[127]]
def d_write_eachtags(fstream,tagstitle,tagsdata):
fstream.write(tagstitle+'\x00\x00\x00')
fstream.write(chr(len(tagsdata)+1)+'\x00\x00\x03')
fstream.write(tagsdata)
return
def d_change_tags2utf8(filename):
"""Change ID3 Tags content from cp874 to utf8"""
fstream=open(filename,"r+b")
ispass=False
if fstream.read(3)=="ID3":
# READ ID3 TAGS DATA
fstream.read(6)
nbyte=ord(fstream.read(1))
ltags=[]
ctagsname=fstream.read(4)
while ctagsname in ["TIT2","TPE1","TALB"]:
fstream.read(3)
ntagsbyte=ord(fstream.read(1))
fstream.read(3)
ctagscontent=fstream.read(ntagsbyte-1)
ltags.append([ctagsname,ntagsbyte,ctagscontent])
ctagsname=fstream.read(4)
#
# CONVERT TO utf8
nnewbyte=0
for eachtags in ltags:
if not '\xe0' in eachtags[2]:
eachtags[2]=d_convert(eachtags[2])
if not ispass:
ispass=True
#
else:
print "File %s already in utf8 format." % (filename)
fstream.close()
return False
#
eachtags[1]=len(eachtags[2])
nnewbyte=nnewbyte+4+3+3+eachtags[1]+1
#
# WRITE BACK CONVERTED DATA
fstream.seek(9)
fstream.write(chr(nnewbyte))
for eachtags in ltags:
d_write_eachtags(fstream,eachtags[0],eachtags[2])
#
if nnewbyte<nbyte:
for i in range(nbyte-nnewbyte):
fstream.write('\x00')
#
#
if ispass:
print "Id3 Tags: file %s converted" % (filename)
#
else:
# CHECK FOR ID3V1
fstream.seek(0)
wholefile=fstream.read(-1)
if 'TAG' in wholefile:
ltags=d_getID3V1data(fstream)
nnewbyte=0
for i in range(len(ltags)):
ltags[i]=d_convert(ltags[i])
if d_passcheck_invalid_char(ltags[i]):
nnewbyte=nnewbyte+len(ltags[i])+4+3
#
#
fstream.close()
fstream=open(filename,"w")
fstream.write('ID3'+'\x04\x00\x00\x00\x00\x08')
fstream.write(chr(nnewbyte))
# Songname 30 3-32 :TIT2
# Artist 30 33-62 :TPE1
# Album 30 63-92 :TALB
# Year 4 93-96 :TDOR
# Comment 30 97-126 :COMM
# Genre 1 127 :----
print ltags[0]
if d_passcheck_invalid_char(ltags[0]):
d_write_eachtags(fstream,'TIT2',ltags[0])
if d_passcheck_invalid_char(ltags[1]):
d_write_eachtags(fstream,'TPE1',ltags[1])
if d_passcheck_invalid_char(ltags[2]):
d_write_eachtags(fstream,'TALB',ltags[2])
if d_passcheck_invalid_char(ltags[3]):
d_write_eachtags(fstream,'TDOR',ltags[3])
if d_passcheck_invalid_char(ltags[4]):
d_write_eachtags(fstream,'COMM',ltags[4])
# DISCARD Genre TAGS
for i in range(1016):
fstream.write('\x00')
#
fstream.write(wholefile)
else:
print "ID3 Tags not found in %s" % (filename)
#
#
fstream.close()
def process_dir(dir):
"""Process all files in the folder"""
for f in os.listdir(dir):
file = dir + os.sep + f
if os.path.isdir(file):
print "Enter directory %s" % (file)
process_dir(file)
print "---exit directory %s" % (file)
#
if f[-4:]==skel:
# DO CONVERT FILENAME
file=d_rename2utf8(dir,file)
# DO CHANGE ID3 TAGS
d_change_tags2utf8(file)
#
#
return
def main():
"""main routine"""
process_dir('.')
return
if __name__=='__main__':
main()
มีโจทย์อยู่คือ
เวลาไปเที่ยวหรือมีงานที่ต้องถ่ายภาพเป็นจำนวนมาก เกินการ์ดหน่วยความจำที่มีอยู่
เวลาการ์ดเต็ม ก็ต้องถ่ายออกมาเก็บไว้ในโน๊ตบุ๊ก
ปัญหาคือเวลาจะดูภาพจากโน๊ตบุ๊ก ซึ่งสเปคเครื่องต่ำมาก โหลดไฟล์ภาพใหญ่ ๆ ไม่ไหว มันจะดูได้ช้ามาก ๆ ดูภาพ 10 ภาพ ใช้เวลาไป 15 นาที
ทางแก้คือคัดลอกไฟล์ภาพมาแปลงเป็นไฟล์เล็ก (อาจจะแปลงด้วย gimp หรือ imagemagick ก็ได้) แต่เนื่องจากสเปคเครื่องต่ำมาก แปลงไฟล์แต่ละครั้งกินเวลาเป็นชั่วโมง ไม่ทันต่อเหตุการณ์
ทางออกอีกทางคือไปแตกเอาไฟล์ JPG อันเล็ก ที่ซ่อนอยู่ภายใต้ไฟล์ตัวจริงซึ่งใหญ่มาก เอาออกมาแทน วิธีนี้จะทำงานได้รวดเร็วกว่ามาก
เคยเขียน C ไว้เป็นไฟล์เล็ก ๆ บนวินโดวส์ แต่เที่ยวนี้ผมลองเอามาคอมไพล์บนลินุกส์ ปรากฎว่าคอมไพล์ไม่ผ่าน และภาษา C ก็ลืมสิ้นแล้ว อย่ากระนั้นเลย พึ่งไพธอนดีกว่า
ผมใช้กล้อง Canon และเราจะแตกไฟล์ JPG อันเล็ก ซึ่งเป็นไฟล์ลำดับที่ 3 ที่ซ่อนอยู่ในไฟล์ใหญ่ เลยตั้งชื่อโปรแกรมว่า canon3.py
เวลาใช้งานก็สั่ง
$ ./canon3.py FILENAME.JPG
ก็จะแตกไฟล์ FILENAME.JPG ไปเป็น canon3/FILENAME.JPG
หรือถ้าสั่ง
$ ./canon3.py เฉย ๆ
ก็จะควานหาทุกไฟล์ในไดเรกทอรี่ที่เป็น JPG หรือ CRW และแตกไฟล์ย่อยออกมาใส่ในไดเรกทอรี่ย่อย canon3
โดยโปรแกรมจะคัดลอกเอาข้อมูล Exif ของกล้องติดไปด้วย (แต่ข้อมูลขนาดภาพใน Exif จะผิดจากความเป็นจริง ขี้เกียจแก้แล้วอ่ะ)
ทดลองแล้วความเร็วใสการแตกไฟล์ดีมาก (ไพธอนนี่ดีกว่าที่คิดเยอะเลย)
โค๊ดมีดังนี้
#!/usr/bin/env python
# EXTRACT THIRD jpg FILE IN Canon CAMERA
#
# jpg file format
# start with: FF D8 FF E1 NN NN ... (contain exif data to byte NN NN)
# first JPG : FF D8 ... FF D9 (thumbnail)
# second JPG: FF D8 ... FF D9 (real JPG)
# third JPG : FF D8 ... FF D9 (hidden small JPG)
# SKIP TO 2/3 OF FILE THEN SEARCH FOR THIRD #FFD8 TO #FFD9
#
import sys, os, string
# VAR
tag_beg="\xff\xd8"
tag_end="\xff\xd9"
subdir="canon3"
file_skel=[".JPG",".jpg",".jpeg"]
dir_skel=["DCIM","CANON"]
# PROCEDURE
def process_file(filename):
for fskel in file_skel:
if fskel in filename:
#OPEN FILE IN BINARY MODE
try:
f = open(filename, 'rb')
except:
print 'Could not open file to read !', filename
sys.exit(3)
if f is None:
print 'Error opening file ', filename
sys.exit(3)
# COPY EXIF HEADING TO NEW FILE IN SUBDIR canon3
basename=os.path.basename(filename)
print "%s -> %s/%s" % (basename,subdir,basename)
if not os.path.exists(subdir):
os.mkdir(subdir)
#
f_new=open(os.path.join(subdir,basename),"wb")
# SEEK FF D8 FF E1 NN NN
f.seek(4)
offset_low=f.read(1)
offset_hi=f.read(1)
no_of_byte=ord(offset_low)*256+ord(offset_hi)-4
## print "no_of_byte=%i" % no_of_byte
# WRITE TO NEW FILE
f_new.write("\xff\xd8\xff\xe1"+offset_low+offset_hi+f.read(no_of_byte))
# SEEK FOR THE REST 3RD JPG FILE
# INCREASE SPEED BY SKIP TO 2/3 OF FILE
f.seek(os.path.getsize(filename) * 2/3)
is_found=False
for i in f:
if tag_beg in i:
is_found=True
# WRITE THIRD JPG TO NEW FILE
f_new.write(tag_beg+i.split(tag_beg)[1])
for j in f:
if tag_end in j:
f_new.write(j.split(tag_end)[0]+tag_end)
break
f_new.close()
else:
f_new.write(j)
#
#
break
#
#
f.close()
if not is_found:
print "third JPG file not found."
#
#
#
def process_dir(dirname):
## print "dirname=%s" % dirname
for dskel in dir_skel:
if dskel in dirname:
print "enter %s" % dirname
os.chdir(dirname)
filelist=os.listdir(".")
for i in filelist:
if os.path.isdir(i):
process_dir(i)
else:
process_file(i)
#
#
print "exit %s" % dirname
os.chdir("..")
#
#
# MAIN PROG
def main():
if len(sys.argv) < 2:
# PROCESS ALL FILE&DIR
for filelist in os.listdir("."):
if os.path.isdir(filelist):
process_dir(filelist)
else:
process_file(filelist)
#
#
else:
filename = os.path.abspath(sys.argv[1])
if os.path.isdir(filename):
process_dir(filename)
else:
process_file(filename)
#
#
print "finished"
if __name__=="__main__":
main()
แถมอีกนิด
ถ้าจะให้หมุนภาพอัตโนมัติด้วย ต้องใช้แพคเกจ jhead
$ sudo apt-get install jhead
$ jhead -autorot *
ลดขนาดรูปพร้อมกับหมุนภาพอัตโนมัติ ทำลึกลงไปทุกไดเรคทอรี่
$ sudo apt-get install imagemagick jhead
$ sudo touch /usr/local/bin/d.canon5.py
$ sudo chmod 0755 /usr/local/bin/d.canon5.py
$ sudo vi /usr/local/bin/d.canon5.py
#!/usr/bin/env python
import sys, os, string
# VAR
subdir="canon3"
file_skel=[".JPG",".jpg",".jpeg"]
dir_skel=["DCIM","CANON","100EOS5D"]
# PROCEDURE
def process_file(filename):
for fskel in file_skel:
if fskel in filename:
if not os.path.exists(subdir):
os.mkdir(subdir)
#
print "convert %s -> %s" % (filename, os.path.join(subdir,filename))
os.system('convert -resize 40%'+" %s %s" % (filename,os.path.join(subdir,filename)))
os.system("jhead -autorot %s" % (os.path.join(subdir,filename)))
#
#
def process_dir(dirname):
## print "dirname=%s" % dirname
for dskel in dir_skel:
if dskel in dirname:
print "enter %s" % dirname
os.chdir(dirname)
filelist=os.listdir(".")
for i in filelist:
if os.path.isdir(i):
process_dir(i)
else:
process_file(i)
#
#
print "exit %s" % dirname
os.chdir("..")
#
#
# MAIN PROG
def main():
if len(sys.argv) < 2:
# PROCESS ALL FILE&DIR
for filelist in os.listdir("."):
if os.path.isdir(filelist):
process_dir(filelist)
else:
process_file(filelist)
#
#
else:
filename = os.path.abspath(sys.argv[1])
if os.path.isdir(filename):
process_dir(filename)
else:
process_file(filename)
#
#
print "finished"
if __name__=="__main__":
main()
ได้ลองใช้ ufraw แล้วรู้สึกว่าสีสวยกว่า dcraw
ติดตั้งด้วย
$ sudo aptitude install ufraw
ทำงานแบบบรรทัดคำสั่ง
$ cd /PATH/TO/IMAGE
$ mkdir jpg
$ for i in *cr2; do
ufraw-batch \
--wb=camera \
--exposure=auto \
--out-type=jpeg \
--compression=96 \
--out-path=./jpg \
$i
done
เป็นการทำงานกับไฟล์ภาพนามสกุล cr2 โดยให้ผลิตไฟล์ jpg ไปอยู่ในโฟลเดอร์ชื่อ jpg
เอามาจาก
Linux Photography : Workflow (3) - quick RAW converting batch
เพิ่งได้เริ่มหัดถ่ายภาพแบบ Raw ของกล้อง Canon มีนามสกุลเป็น .CR2
$ sudo aptitude install gimp gimp-ufraw$ sudo aptitude install dcraw
$ for i in *.CR2; do dcraw -c -q 3 -B 2 4 -w $i | cjpeg -quality 100 > `basename $i .CR2`.jpg; done
เอามาจาก How to convert raw cr2 pictures with linux, and merge pictures by date and Exif data with jhead
ทำตัวอย่างการค้นเนื้อหาไพธอน (20%)
เสร็จประมาณ 10% แล้ว
อยากให้ทำงานได้แบบ php help จัง
update
ต่อเนื่องจากครั้งก่อนเรื่อง imagemagick: ทำ annotate ไฟล์ tif
มีข้อบกพร่องเล็กน้อยคือ เวลาที่ inkscape นำเข้าไฟล์ tif จะกำหนดขนาดเป็นพิกเซลตามไฟล์ tif
ซึ่งจะมีขนาดใหญ่กว่า A4 พอสมควร (เทียบที่ค่าปริยายของ inkscape คือ 90dpi)
ถ้าจะแก้ไขด้วยการปรับหน้ากระดาษและลดขนาดภาพใน inkscape ทุกครั้ง ก็ดูไม่ค่อยสะดวก
และจะแก้ไขด้วยการลดขนาดพิกเซลของภาพ ก็เสียดายความละเอียด
จึงทดลองแปลงสคริปต์ไฟล์เดิม จากการใช้เชลล์สคริปต์ของ bash มาเป็นไพธอนแทน ทั้งนี้เพียงเพื่อหาขนาดพิกเซลของภาพเท่านั้น (แทบไม่ได้ใช้ความสามารถจริง ๆ ของไพธอนเลย)
วิธีการใช้แบบโกง ๆ หน่อย คือในครั้งแรกที่แตกไฟล์ เราสร้างไฟล์ svg ซึ่งเป็นไฟล์ xml ขึ้นมาเองเลย
โดยให้เขียนค่าพิกเซลที่เหมาะสม คือลดหรือเพิ่มขนาดให้ไฟล์ภาพลงบนความกว้างของหน้ากระดาษ A4 ได้
อย่าลืมต้องติดตั้งแพกเกจที่ต้องการก่อน
$ sudo aptitude install imagemagick inkscape evince python python-imaging
เนื้อไฟล์ตั้งชื่อว่า d.tifannotate.py มีดังนี้
$ sudo vi /usr/local/bin/d.tifannotate.py
#!/usr/bin/env python
# ANNOTATE MULTIPLE PAGES TIF FILE
# PREREQUISITE: inkscape imagemagick evince python-imaging
import sys
import os
import commands
try:
import Image
except:
print "Missing PIL library, require packages python-imaging."
sys.exit(1)
#PARAMETER
overwrite_new_file = True #OVERWRITE image-new.tif
__width = 744.09448819 #DEFAULT A4 SIZE IN PIXEL
__height = 1052.3622047 #
#REQUIRE PACKAGES
packages = ( ("inkscape","inkscape"), ("convert","imagemagick"), ("evince","evince") )
for i in range(len(packages)):
exec("status, %s = commands.getstatusoutput('which %s')" % (packages[i][0], packages[i][0]) )
if status != 0:
print "Missing command %s, require packages %s." % (packages[i][0], packages[i][1],)
sys.exit(1)
#GLOBAL VARS
err_open = "Cannot open file %s."
#PROCEDURE
def usage(progname):
print "Annotate multiple pages TIF image."
print "Usage: %s IMGFILE PAGES..." % (progname)
print "Ex1 : %s image.tif 0 2 = Edit image.tif page 0 and page 2." % (progname)
def errmsg(msg, *arg):
if not '%s' in msg:
return msg
if type(arg)!=type([]):
return msg % (arg)
lmsg = msg.split('%s')
for i in range(len(arg)):
lmsg[1] = lmsg[0] + str(arg[i]) + lmsg[1]
lmsg.pop(0)
return '%s'.join(lmsg)
def genfilename(filename="",tail="new",overwrite=True):
if filename=="":
return ""
if tail.lower()=="new":
tail="new"
if tail.lower()!="new" and tail.lower()!="bak":
tail="bak"
name,ext = os.path.splitext(filename)
if not overwrite and os.path.exists(name+'-'+tail+ext):
i=0
while os.path.exists(name+'-'+tail+str(i)+ext) and (i < 1000):
i=i+1
if i>999:
return ""
return name+"-"+tail+str(i)+ext
else:
return name+'-'+tail+ext
def createsvg(tifname):
img = Image.open(tifname)
width, height = img.size
newwidth = __width
newheight = float(height)/width*newwidth
file, ext = os.path.splitext(tifname)
f = open(file+'.svg', 'w')
f.write("""\
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.0"
width="%s"
height="%s"
id="svg2">
<defs
id="defs5" />
<image
xlink:href="%s"
x="0"
y="0"
width="%s"
height="%s"
id="image9" />
</svg>""" % (__width, __height, tifname, newwidth, newheight,))
f.close()
return
#MAIN PROGRAM
def main(tiffile, pages):
newfile=genfilename(tiffile,"new",overwrite_new_file)
if newfile=="":
print "Cannot save backup file."
sys.exit(1)
#SPLIT FILE
name,ext = os.path.splitext(tiffile)
dirname = name+'~'
if not os.path.exists(dirname):
os.mkdir(dirname)
if not os.path.exists(os.path.join(dirname, name+'0'+ext)):
os.system('%s %s %s' % (convert, tiffile, os.path.join(dirname,name)) +'%d'+ ext)
#CREATE svg FILE IN SUBDIR
os.chdir(dirname)
filelist = [i for i in os.listdir(os.path.curdir) if i[-(len(ext)):]==ext ]
for i in filelist:
n,e = os.path.splitext(i)
c = n+'.svg'
if not os.path.exists(c):
createsvg(i)
#EDIT/ADD ANNOTATE
for i in pages:
if int(i) < len(filelist):
c = str(i)
os.system('%s %s.svg' % (inkscape, name+c,))
os.system('%s -e %s.png %s.svg' % (inkscape, name+c, name+c,))
else:
print "Invalid page %s." % (i,)
#MERGE BACK TO NEW NAME:convert img0.svg ... -adjoin -compress lzw ../img-new.tif
allfile = ' '.join( [i for i in os.listdir(os.path.curdir) if i[-(4):]=='.png' ] )
os.system('%s %s -adjoin -compress lzw %s' % (convert, allfile, os.path.join(os.path.pardir,newfile), ))
#CHDIR BACK
os.chdir(os.path.pardir)
print "Annotate %s success, save new file in %s. Viewing with %s" % (tiffile,newfile,evince)
os.system('%s %s' % (evince, newfile,))
if __name__=="__main__":
#sys.argv=[progname tiffile 0 1 2]
progname=os.path.basename(sys.argv[0])
try:
tiffile=sys.argv[1]
except:
usage(progname)
sys.exit(1)
if not os.path.isfile(tiffile):
print errmsg(err_open, tiffile)
sys.exit(1)
if len(sys.argv)<3:
usage(progname)
sys.exit(1)
pages = sys.argv[2:]
main(tiffile, pages)
เรียกใช้เหมือนเดิมคือ
$ d.tifannotate.py IMAGE.tif 0 1
ได้ผลออกมาเป็น IMAGE-new.tif
หมายเหตุ
ถ้าต้องการพิมพ์จาก inkscape ต้องติดตั้งแพกเกจ cupsys-bsd ด้วย
$ sudo aptitude install cupsys-bsd
update 50-09-11
ลองเขียนสคริปต์ลบแสปม
ใช้กับบอร์ด yabbse กับ smf
ของ smf ยังไม่เสร็จ บันทึกเอาไว้เพื่อลองดูผลเท่านั้น
ต้องเปลี่ยนแปลงสคริปต์ตามธีมที่ใช้ด้วย
*** สคริปต์นี้ใช้กับ thailinuxhosting.com/yabbse เท่านั้น เพราะใส่โค๊ดที่แก้ปัญหาบอร์ดไว้ด้วยครับ
#!/usr/bin/env python
# -*- coding: utf-8 -*-
user = "wd"
password = "mypassword"
enc_password = "XXXXXXXXXX" # *** GET ENCRYPTED PASSWORD FROM BROWSER COOKIE
site = "http://www.thailinuxhosting.com/yabbse" #"http://www.thaitux.info/smf"
board = "yabbse" # "smf", "yabbse"
charset = "tis620" # "utf8", "tis620"
max_loop = 5 # = RECENT LIST OF BOARD
root = "/home/wd/spam"
backup_file = root+"/thailinuxhosting-bak.txt"
spamtext_file = root+"/spamlist.txt"
cookie_file = root+"/thailinuxhosting-cookie"
import sys
import os
import time
##### PRE RUN FOR RETRIEVE COOKIE #####
import urllib2
import cookielib
login = "/index.php?action=login2;user=%s;passwrd=%s;cookielength=302400" % (user, password,)
cj = cookielib.MozillaCookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
sock = opener.open(site+login)
cj.save(cookie_file, ignore_discard=True, ignore_expires=True)
sock.close()
#######################################
spamlist = []
def decoding(txt):
if charset == "tis620":
return txt.decode("utf8").encode("tis620")
elif charset == "utf8":
return txt
else:
print "Error, CHARSET is not defined"
sys.exit[0]
def search_line(txt, l, occur=1):
for i in range(len(l)):
if txt in l[i]:
if occur > 1:
occur=occur-1
else:
return i
return -1
def get_msgid(url):
if board == "smf": # ...#msgXX
return url.split("#msg")[-1]
elif board == "yabbse": # ...;start=XX
return url.split(";start=")[-1]
def check_spam(txt):
global spamlist
for i in spamlist:
if i in txt:
return True, i
return False, ''
def save_backup(txt):
f = open(backup_file,'a')
f.write(txt+'\n\n\n')
f.close()
return
def die_board():
print "board not exist"
sys.exit[0]
if board == "smf":
# recent_str = "กระททู้เมมื่อเร็วๆ นนี้"
recent_str = "\xe0\xb8\x81\xe0\xb8\xa3\xe0\xb8\xb0\xe0\xb8\x97\xe0\xb8\xb9\
\xe0\xb9\x89\xe0\xb9\x80\xe0\xb8\xa1\xe0\xb8\xb7\xe0\xb9\x88\xe0\xb8\xad\
\xe0\xb9\x80\xe0\xb8\xa3\xe0\xb9\x87\xe0\xb8\xa7\xe0\xb9\x86 \xe0\xb8\x99\
\xe0\xb8\xb5\xe0\xb9\x89"
elif board == "yabbse":
# recent_str = "โพสต์เมมื่อเร็วๆนนี้"
recent_str = "\xe0\xb9\x82\xe0\xb8\x9e\xe0\xb8\xaa\xe0\xb8\x95\xe0\xb9\x8c\
\xe0\xb9\x80\xe0\xb8\xa1\xe0\xb8\xb7\xe0\xb9\x88\xe0\xb8\xad\xe0\xb9\x80\
\xe0\xb8\xa3\xe0\xb9\x87\xe0\xb8\xa7\xe0\xb9\x86\xe0\xb8\x99\xe0\xb8\xb5\xe0\xb9\x89"
else:
die_board()
#LOAD SPAM DATA
if not os.path.exists(spamtext_file):
f = open(spamtext_file,'w')
f.close()
f = open(spamtext_file)
for i in f:
if i!='' and len(i)>3:
spamlist.append(decoding(i.strip()))
f.close()
recent_str = decoding(recent_str)
#INIT COOKIE & OPENER
cj = cookielib.MozillaCookieJar()
cj.load(cookie_file)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
loop_count = 0
url_list_pair = [] #SOLVE yabbse ONLY INDEX TO LAST MESSAGE, SO WE CREATE OUR OWN
while loop_count < max_loop:
#FIRST PAGE
sock = opener.open(site)
#HACK: SOLVE yabbse'S BOARD COOKIE ERROR
cj._cookies['thailinuxhosting.com']['/yabbse']['YaBBSE140usernamev14'].value = user
cj._cookies['thailinuxhosting.com']['/yabbse']['YaBBSE140passwordv14'].value = enc_password
html = sock.read()
sock.close()
l = html.split('\n')
#SESSIONID
if board == "smf":
sstr = "sesc="
line = search_line(sstr, l)
if line < 0:
sys.exit[0]
session_id = l[line].split(sstr)[1].split('">')[0]
else:
session_id = ""
#SEARCH FOR RECENT POST
sstr = recent_str
line = search_line(sstr, l)
if line < 0:
sys.exit[0]
if board == "smf":
url = l[line+9+loop_count].split('<a href="')[1].split('">')[0]
author = ""
date_submitted = ""
elif board == "yabbse":
url = l[line+4+loop_count].split('<td valign="top"><a href="')[1].split('">')[0]
url_list = [ i[0] for i in url_list_pair ] # SOLVE yabbse MESSAGE INDEX
if url in url_list:
i = url_list.index(url)
url_list_pair[i][1] += 1
index_dec = url_list_pair[i][1]
else:
url_list_pair.append([url,0])
index_dec = 0
# tmp = 'โดย '
tmp = decoding('\xe0\xb9\x82\xe0\xb8\x94\xe0\xb8\xa2 ')
author = l[line+4+loop_count].split(tmp)[1].split('</td>')[0]
else:
die_board()
msgid = get_msgid(url)
sock = opener.open(url)
html = sock.read()
sock.close()
l = html.split('\n')
#PARSE HTML
is_spam = False
spam_keyword = ''
if board == "smf":
sstr = "msg_%s" % (msgid,)
line = search_line(sstr, l)
elif board == "yabbse":
sstr = '<hr width="100%" size="1" class="windowbg3">'
count = (int(msgid)-index_dec) % 20 + 1 # 20 MESSAGES PER PAGE - yabbse INDEX DECREMENT
print 'loop=',loop_count,' /// count=',count
line = search_line(sstr, l, count)
tmp = decoding("javascript:DoConfirm('")
try:
delete_url = l[line-3].split(tmp)[1].split("','")[1].split("""');"><img src""")[0]
date_submitted = l[line-4].split('</B> ')[1].split(' »')[0]
title = l[line-5].split('<B>')[1].split('</b>')[0]
process_line = line+1
is_spam, spam_keyword = check_spam(l[process_line])
if is_spam:
print 'line=',line,' /// l[line-3]=', l[line-3]
print 'delete_url=',delete_url
print "is_spam=",is_spam," /// keyword=",spam_keyword," /// line=",l[process_line]
except:
is_spam = False
else:
die_board()
if is_spam:
if board == "smf":
pass
elif board == "yabbse":
save_backup('delete url: '+delete_url+\
'\nspam keyword: '+spam_keyword+\
'\nscan date: '+time.ctime(time.time())+\
'\ntitle: '+title+\
'\nauthor: '+author+\
'\nsubmitted date: '+date_submitted+\
'\n'+l[process_line])
sock = opener.open(delete_url)
sock.close()
url_list = [ i[0] for i in url_list_pair ] # RESET yabbse MESSAGE INDEX
if url in url_list:
i = url_list.index(url)
url_list_pair.remove(url_list_pair[i])
else:
loop_count = loop_count+1
เขียนสคริปต์ไว้เพื่อให้เปลี่ยนรุ่น Drupal ง่าย ๆ เผื่อมีหลายไซต์
สมมุติว่าไดเรกทอรี่ติดตั้งอยู่ที่ /var/www/drupal
URL คือ http://www.example.com
ฐานข้อมูลชื่อ DATABASE_NAME
ผู้ใช้ชื่อ ADMIN และรหัสผ่านคือ ADMIN_PASSWORD
อย่าลืมต้องให้ ADMIN อ่านได้เท่านั้น เพราะจะมีรหัสผ่านอยู่ในสคริปต์
$ cd /var/www/drupal
$ touch sed.py
$ chmod 700 sed.py
ขั้นตอน upgrade
$ cd /var/www/drupal
$ wget http://ftp.drupal.org/files/projects/drupal-X.X.tar.gz
$ tar xfx drupal-X.X
$ cd drupal-X.X
$ cp -xa * ..
$ cd ..
$ rm -rf drupal-X.X
$ ./sed.py
### DO UPDATE AT www.example.com/update.php
### SET BACK
$ vi update.php
.. $access_check = TRUE; #$access_check = FALSE; ...
เนื้อไฟล์ sed.py มีดังนี้
$ vi sed.py
#!/usr/bin/env python
# cd /var/www/drupal
# cp -xa ../drupal-5.3/* .
# ./sed.py
# ---> http://www.example.com/update.php
# vi update.php #SET $access_check = TRUE;
import os
import sys
db_url = "$db_url = 'mysql://ADMIN:ADMIN_PASSWORD@localhost/DATABASE_NAME';"
base_url = "$base_url = 'http://www.example.com'; // NO trailing slash!"
basedir = os.path.abspath(os.curdir);
#-------------------------------------------------------------------------
def sed_file(file,dict_txt):
filename = os.path.basename(file)
dirname = os.path.abspath(file)
bakfile = file+'.bakbak'
os.rename(file,bakfile)
f_old = open(bakfile)
f_new = open(file,'w')
txt_old = [i for i in dict_txt]
txt_new = [i for i in dict_txt.values()]
print txt_old
for i in f_old:
line = i.strip()
if line in txt_old:
n = txt_old.index(line)
print 'old=',line
print 'new=',txt_new[n]
f_new.write(txt_new[n]+'\n')
else:
f_new.write(i)
f_new.close()
f_old.close()
#FIRST FILE sites/default/setting.php
file = os.path.join(basedir,'sites/default/settings.php')
tmp_db = "$db_url = 'mysql://username:password@localhost/databasename';"
tmp_base = "# $base_url = 'http://www.example.com'; // NO trailing slash!"
dict_txt = {\
tmp_db: '#'+tmp_db+'\n'+db_url, \
tmp_base: tmp_base+'\n'+base_url \
}
sed_file(file,dict_txt)
#SECOND update.php
file = os.path.join(basedir,'update.php')
dict_txt = {\
"$access_check = TRUE;":\
"#$access_check = TRUE;\n$access_check = FALSE;"\
}
sed_file(file,dict_txt)
มีงานที่จะต้องทำไฟล์เป็น pdf เพื่อส่งโรงพิมพ์
งานนี้ทำจาก Word ในวินโดวส์ พิมพ์ลงไฟล์โดยใช้ไดรฟเวอร์เครื่องพิมพ์ Image Setter แล้วจึงแปลงเป็น pdf ด้วยลินุกซ์ ด้วยคำสั่ง ps2pdf12 โดยเลือกใช้รุ่น 1.2 เพราะต้องการความเข้ากันได้
แต่เนื่องจากขนาดกระดาษของงานเป็นขนาด A5 จึงต้องเลือกพิมพ์เป็น A4 แทน
ปัญหาคือตัวโปรแกรม ps2pdf ซึ่งไปเรียกใช้ ghostscript (gs) อีกทีนึง ไม่สามารถ crop ขนาดจาก A4 เป็น A5 ได้ (จริง ๆ แล้วอาจทำได้ แต่ค้นคำสั่งไม่พบ และโรงพิมพ์ต้องการงานขนาด A5 แบบมีขอบขาวเว้นไว้ด้านละ 3 มม. ซึ่งคงจะใช้คำสั่ง gs ยาก)
ค้นไปค้นมา พบมอดูลไพธอนที่จะทำงานนี้ได้ คือมอดูล pyPdf
เริ่มเลยแล้วกัน
ติดตั้งมอดูล pyPdf
$ sudo aptitude install python-pypdf
เขียนสคริปต์ ตั้งชื่อว่า croppdf.py
$ vi croppdf.py
#!/usr/bin/env python
#prerequisites: aptitude install python-pypdf
import sys
import pyPdf
def usage(progname):
print """
usage: %s "lowerLeft-x lowerLeft-y upperRight-x upperRight-y" infile.pdf outfile.pdf
""" % progname
sys.exit(1)
try:
argl = [ int(i) for i in sys.argv[1].split(" ") if i ]
infile = sys.argv[2]
outfile = sys.argv[3]
inpdf = pyPdf.PdfFileReader(file(infile,"rb"))
outpdf = pyPdf.PdfFileWriter()
for i in range(inpdf.numPages):
page = inpdf.getPage(i)
page.mediaBox.upperRight = tuple(argl[2:])
page.mediaBox.lowerLeft = tuple(argl[:2])
outpdf.addPage(page)
outstream = file(outfile, "wb")
outpdf.write(outstream)
outstream.close()
except:
usage(sys.argv[0])
$ chmod 755 croppdf.py
(พอดีเป็นงานด่วน เลยเขียนแบบด่วนจริง ๆ)
ขั้นตอนการแปลงคือ
1. แปลงจาก ps เป็น pdf ด้วย ps2pdf12
$ ps2pdf12 INFILE.ps TEMPFILE.pdf
2. crop เป็นขนาด A5 แบบมีขอบขาวข้างละ 3 มม. (ประมาณ 9 px)
$ ./croppdf.py "75 238 523 850" TEMPFILE.pdf OUTFILE.pdf
ตัวเลข 4 ตัวคือค่าเป็นปอยต์ (pt) ของ x-มุมล่างซ้าย y-มุมล่างซ้าย และ x-มุมบนขวา y-มุมบนขวา ตามลำดับ
แปลงจาก มม. โดยคูณด้วย 2.8378
หรือแปลงจากนิ้ว โดยคูณด้วย 72
ดูขนาดเอกสาร เป็นปอยต์ ด้วยคำสั่ง pdfinfo FILENAME.pdf
สมมุติว่า lowerLeft-x เป็น x0, lowerLeft-y เป็น y0, upperRight-x เป็น x1, upperRight-y เป็น y1 ตามลำดับ
$ ./croppdf.py "-14 -14 609 856" TEMPFILE.pdf OUTFILE.pdf
$ ./croppdf.py "-14 -14 434 609" TEMPFILE.pdf OUTFILE.pdf
$ ./croppdf.py "87 247 508 842" TEMPFILE.pdf OUTFILE.pdf
$ ./croppdf.py "8 -25 604 817" TEMPFILE.pdf OUTFILE.pdf
$ ./croppdf.py "0 -25 595 817" TEMPFILE.pdf OUTFILE.pdf
เสร็จแล้วครับ
ที่มาจริง ๆ แล้ว ต้องการค้นหาและแทนที่เอกสารในไฟล์ .doc จึงสั่งด้วยคำสั่งว่า
$ sed -i 's/OLD/NEW/g' *.doc
ไม่ได้ผล นึกว่ารหัสเอกสารผิด เลยเป็นที่มาของสคริปต์อันนี้ คือค้นหาและแทนที่เอกสารทั้ง utf-8 และ tis-620 โดยไม่สนใจว่าเป็นเอกสารชนิดใด
(สุดท้ายปรากฎว่าไม่ได้ผล เพราะ OpenOffice ไม่ได้เก็บไว้ในรูป Text file ปกติ
ถึงจะโง่ไปแล้ว แต่บันทึกไว้หน่อยดีกว่า เผื่อได้ใช้ทีหลัง)
$ vi sed_i.py
#!/usr/bin/env python
# -*- coding: utf8 -*-
"""Replace thai string in file"""
import sys, os
def usage(prog):
print 'Usage: %s "old" "new" filename' % (prog)
def cannotopenfile(filename):
print "Cannot open file %s" % (filename)
def getbakfilename(filename="", ext="bak"):
if filename == "":
return ""
if os.path.exists(filename + "." + ext):
i = 0
while os.path.exists(filename + "." + ext + str(i)) and (i < 1000):
i += 1
if i > 999:
return ""
return filename + "." + ext + str(i)
else:
return filename + "." + ext
#
def main(old,new,filename):
if not os.path.exists(filename):
cannotopenfile(filename)
sys.exit(0)
ismod = False
newdoc = []
f = open(filename)
for line in f:
newline1 = line.replace(old, new)
newline2 = line.replace(old.decode('utf8').encode('tis620'), new.decode('utf8').encode('tis620'))
if not ismod and (newline1 != line or newline2 != line):
ismod = True
if newline1 != line:
newdoc.append(newline1)
else:
newdoc.append(newline2)
f.close()
if ismod:
bakfile = getbakfilename(filename)
os.rename(filename, bakfile)
f = open(filename, "w")
f.write('\n'.join(newdoc))
f.close()
print "%s changed, save backup in %s." % (filename, bakfile)
else:
print "Pattern not found, no changed."
if __name__ == "__main__":
if len(sys.argv) < 4:
usage(sys.argv[0])
sys.exit[0]
else:
main(*sys.argv[1:4])
ใช้งานด้วยคำสั่ง
$ ./sed_i.py "OLD" "NEW" filename
ถ้าเจอ จะแทนที่ และบันทึกไว้ในชื่อเดิม แต่สำรองไฟล์ไว้ด้วย ในชื่อ filename.bak
จากครั้งก่อนเรื่อง adodb: กับดักข้อมูล ที่ได้นำเอาโมดูล DBF Reader จากเว็บของคุณ Yusdi Santoso มาทดลองใช้งาน
เมื่อได้นำมาใช้จริง สำหรับไฟล์ dbf ของ Visual Foxpro สามารถใช้ได้ผลดีพอควร แต่สำหรับ dbf เก่า ๆ ที่เป็นของ Foxpro for Dos นั้น ปรากฎว่าไม่สามารถอ่านได้
อีกเรื่องนึงคือเรื่องชื่อไฟล์ในลินุกซ์ เมื่อใช้งานผ่าน samba (เมานต์แบบ cifs) แล้ว เข้าใจว่าไม่สามารถควบคุมได้ บางครั้งปรากฎเป็นตัวใหญ่หมด บางครั้งออกมาเป็นชื่อไฟล์ตัวใหญ่แต่นามสกุลตัวเล็ก หรือกลับกัน เราจึงมาปรับปรุงในส่วนนี้ด้วย
ได้ออกมาเป็นซอร์สไฟล์ดังนี้ครับ
$ vi dbf.py
#!/usr/bin/env python
"""
This is a DBF reader which reads Visual Fox Pro DBF format with Memo field.
Usage:
rec = readDbf('test.dbf')
for line in rec:
print line['name']
@author Yusdi Santoso
@date 13/07/2007
"""
import struct
import os, os.path
import sys
import csv
import tempfile
import ConfigParser
class Dbase:
def __init__(self):
self.fdb = None
self.fmemo = None
self.db_data = None
self.memo_data = None
self.fields = None
self.num_records = 0
self.header = None
self.memo_file = ''
self.memo_header = None
self.memo_block_size = 0
self.memo_header_len = 0
def _drop_after_NULL(self, txt):
for i in range(0, len(txt)):
if ord(struct.unpack('c', txt[i])[0])==0:
return txt[:i]
return txt
def _reverse_endian(self, num):
if not len(num):
return 0
#OLD CODE
#val = struct.unpack('<L', num)
#val = struct.pack('>L', val[0])
#val = struct.unpack('>L', val)
#return val[0]
#wd's IMP: IMPROVE READING OLD FOXPRO MEMO
try: #VFP DBF: BINARY 4 BYTES MEMO FIELD REF
val = struct.unpack('<L', num)
val = struct.pack('>L', val[0])
val = struct.unpack('>L', val)
return val[0]
except: #OLD FOXPRO DBF: STRING 10 BYTES MEMO FIELD REF
val = long('0'+num.strip())
return val
def _assign_ids(self, lst, ids):
result = {}
idx = 0
for item in lst:
id = ids[idx]
result[id] = item
idx += 1
return result
def open(self, db_name):
filesize = os.path.getsize(db_name)
if filesize <= 68:
raise IOError, 'The file is not large enough to be a dbf file'
self.fdb = open(db_name, 'rb')
self.memo_file = ''
#OLD CODE
#if os.path.isfile(db_name[0:-1] + 't'):
# self.memo_file = db_name[0:-1] + 't'
#elif os.path.isfile(db_name[0:-3] + 'fpt'):
# self.memo_file = db_name[0:-3] + 'fpt'
#wd's IMP: SOLVE MISMATCHED UPPER/LOWER FILENAME AND EXTENSION
basename = os.path.basename(db_name)
dirname = os.path.dirname(os.path.join('.',db_name))
allfile=[i for i in os.listdir(dirname) if i[:-4].lower()==basename[:-4].lower()]
for i in allfile:
if i[:-1].lower()==basename[:-1].lower() and i[-1].lower()=='t':
self.memo_file = os.path.join(dirname, i)
elif i[:-3].lower()==basename[:-3].lower() and i[-3:].lower()=='fpt':
self.memo_file = os.path.join(dirname, i)
if self.memo_file:
#Read memo file
self.fmemo = open(self.memo_file, 'rb')
self.memo_data = self.fmemo.read()
self.memo_header = self._assign_ids(struct.unpack('>6x1H', self.memo_data[:8]), ['Block size'])
block_size = self.memo_header['Block size']
if not block_size:
block_size = 512
self.memo_block_size = block_size
self.memo_header_len = block_size
memo_size = os.path.getsize(self.memo_file)
#Start reading data file
data = self.fdb.read(32)
self.header = self._assign_ids(struct.unpack('<B 3B L 2H 20x', data), ['id', 'Year', 'Month', 'Day', '# of Records', 'Header Size', 'Record Size'])
self.header['id'] = hex(self.header['id'])
self.num_records = self.header['# of Records']
data = self.fdb.read(self.header['Header Size']-34)
self.fields = {}
x = 0
header_pattern = '<11s c 4x B B 14x'
ids = ['Field Name', 'Field Type', 'Field Length', 'Field Precision']
pattern_len = 32
for offset in range(0, len(data), 32):
if ord(data[offset])==0x0d:
break
x += 1
data_subset = data[offset: offset+pattern_len]
if len(data_subset) < pattern_len:
data_subset += ' '*(pattern_len-len(data_subset))
self.fields[x] = self._assign_ids(struct.unpack(header_pattern, data_subset), ids)
self.fields[x]['Field Name'] = self._drop_after_NULL(self.fields[x]['Field Name'])
self.fdb.read(3)
if self.header['# of Records']:
data_size = (self.header['# of Records'] * self.header['Record Size']) - 1
self.db_data = self.fdb.read(data_size)
else:
self.db_data = ''
self.row_format = '<'
self.row_ids = []
self.row_len = 0
for key in self.fields:
field = self.fields[key]
self.row_format += '%ds ' % (field['Field Length'])
self.row_ids.append(field['Field Name'])
self.row_len += field['Field Length']
def close(self):
if self.fdb:
self.fdb.close()
if self.fmemo:
self.fmemo.close()
def get_numrecords(self):
return self.num_records
def get_record_with_names(self, rec_no):
"""
This function accept record number from 0 to N-1
"""
if rec_no < 0 or rec_no > self.num_records:
raise Exception, 'Unable to extract data outside the range'
offset = self.header['Record Size'] * rec_no
data = self.db_data[offset:offset+self.row_len]
record = self._assign_ids(struct.unpack(self.row_format, data), self.row_ids)
if self.memo_file:
for key in self.fields:
field = self.fields[key]
f_type = field['Field Type']
f_name = field['Field Name']
c_data = record[f_name]
if f_type=='M' or f_type=='G' or f_type=='B' or f_type=='P':
c_data = self._reverse_endian(c_data)
if c_data:
record[f_name] = self.read_memo(c_data-1).strip()
else:
record[f_name] = c_data.strip()
return record
def read_memo_record(self, num, in_length):
"""
Read the record of given number. The second parameter is the length of
the record to read. It can be undefined, meaning read the whole record,
and it can be negative, meaning at most the length
"""
if in_length < 0:
in_length = -self.memo_block_size
offset = self.memo_header_len + num * self.memo_block_size
self.fmemo.seek(offset)
if in_length<0:
in_length = -in_length
if in_length==0:
return ''
return self.fmemo.read(in_length)
def read_memo(self, num):
result = ''
buffer = self.read_memo_record(num, -1)
if len(buffer)<=0:
return ''
length = struct.unpack('>L', buffer[4:4+4])[0] + 8
block_size = self.memo_block_size
if length < block_size:
return buffer[8:length]
rest_length = length - block_size
rest_data = self.read_memo_record(num+1, rest_length)
if len(rest_data)<=0:
return ''
return buffer[8:] + rest_data
def readDbf(filename):
"""
Read the DBF file specified by the filename and
return the records as a list of dictionary.
@param filename File name of the DBF
@return List of rows
"""
db = Dbase()
db.open(filename)
num = db.get_numrecords()
rec = []
for i in range(0, num):
record = db.get_record_with_names(i)
rec.append(record)
db.close()
return rec
if __name__=='__main__':
rec = readDbf('dbf/sptable.dbf')
for line in rec:
print '%s %s' % (line['GENUS'].strip(), line['SPECIES'].strip())
ผลการปรับปรุงปรากฎว่าใช้งานได้ดีพอควรครับ