2017-04-01 13 views
8

मैं किसी Office/एक्सेल दस्तावेज़ अजगर का उपयोग करने से जोड़ सकते हैं और फ़ाइलों को अलग करना चाहते हैं के बिना कार्यालय/एक्सेल दस्तावेज़ से पहुंच एम्बेडेड OLE। अब तक चीजें जोड़ना आसान है लेकिन निकालने के लिए मुझे एक साफ समाधान नहीं मिला है।पायथन: क्लिपबोर्ड

यह स्पष्ट करने के लिए कि मुझे क्या मिला है और मैंने छोटे उदाहरण test.py नीचे और आगे की व्याख्या नहीं की है।

test.py

import win32com.client as win32 
import os 
from tkinter import messagebox 
import win32clipboard 

# (0) Setup 
dir_path = os.path.dirname(os.path.realpath(__file__)) 
print(dir_path) 
excel = win32.gencache.EnsureDispatch('Excel.Application') 
wb = excel.Workbooks.Open(dir_path + "\\" + "test_excel.xlsx") 
ws = wb.Worksheets.Item(1) 
objs = ws.OLEObjects() 

# (1) Embed file 
f = dir_path + "\\" + "test_txt.txt" 
name = "test_txt_ole.txt" 
objs.Add(Filename=f, IconLabel=name) 

# (2) Access embedded file 
obj = objs.Item(1) # Get single OLE from OLE list 
obj.Copy() 
win32clipboard.OpenClipboard() 
data = win32clipboard.GetClipboardData(0xC004) # Binary access 
win32clipboard.EmptyClipboard() 
win32clipboard.CloseClipboard() 
messagebox.showinfo(title="test_txt_ole.txt", message=str(data)) 

# (3) Press don't save here to keep 
# wb.Close() # Will close excel document and leave excel opened. 
excel.Application.Quit() # Will close excel with all opened documents 

तैयारी (कदम 0) के लिए यह खोलता है एक वर्कशीट के साथ किसी दिए गए एक्सेल दस्तावेज है कि एक्सेल में नया दस्तावेज़ बटन का उपयोग करके पहले बनाने था। चरण में (1) यह एक्सेल दस्तावेज में दिए गए पाठ फ़ाइल एम्बेड करने के लिए API का उपयोग करता। टेक्स्ट फ़ाइल को पाठ संपादक का उपयोग करके सामग्री "TEST123" से पहले बनाया गया था। चरण में बाद में (2) इसे वापस एम्बेडेड OLE से सामग्री क्लिपबोर्ड का उपयोग करके पढ़ने के लिए कोशिश करता है और एक संदेश बॉक्स कि क्लिपबोर्ड में OLE से सामग्री से पता चलता खोलता है। अंत में (3) कार्यक्रम खोले दस्तावेज़ को बंद कर देता है। एक अपरिवर्तित सेटअप रखने के लिए यहां कोई दबाएं।

इस समाधान का बड़ा नुकसान यह है जो क्लिपबोर्ड जो एक उत्पादक वातावरण में बुरा शैली है में किसी भी उपयोगकर्ता सामग्री स्मैश क्लिपबोर्ड का उपयोग है। इसके अलावा यह क्लिपबोर्ड के लिए एक अनियंत्रित विकल्प का उपयोग करता है।

एक बेहतर समाधान एक अजगर डेटा कंटेनर के लिए या अपनी पसंद के किसी फ़ाइल के लिए सुरक्षित OLE या OLE एम्बेडेड फाइल करने के लिए किया जाएगा। मेरे उदाहरण में मैंने फ़ाइल डेटा को आसानी से पहचानने के लिए एक TXT फ़ाइल का उपयोग किया है। आखिरकार मैं एक इन-इन-वन समाधान के लिए ज़िप का उपयोग करूंगा लेकिन बेस 64 डेटा के लिए एक TXT फ़ाइल समाधान पर्याप्त होगा।

0xC004 का स्रोत = 49,156: https://danny.fyi/embedding-and-accessing-a-file-in-excel-with-vba-and-ole-objects-4d4e7863cfff

यह VBA उदाहरण दिलचस्प लग रही है लेकिन मैं VBA के बारे में कोई सुराग नहीं है: Saving embedded OLE Object (Excel doc) to file in Excel 2010 vs 2013

+0

तो, मैं भ्रमित सवाल लग रहा है यहाँ मेरी कोड है। ऐसा लगता है कि आप एक्सेल फ़ाइल से कुछ निकालना चाहते हैं, लेकिन मुझे लगता है कि * कुछ * थोड़ा अस्पष्ट होना क्या है। –

+0

मैं अजगर से परिचित नहीं हूं, लेकिन वास्तव में, आप पहले से ही बहुत सारे "वीबीए" कर रहे हैं। 'वर्कबुक। ओपन', 'वर्कशीट्स.इटम' इत्यादि वीबीए कमांड हैं (तकनीकी रूप से वे आईडीस्पैच कॉल हैं)। आप केवल अपने पायथन कोड –

+0

@ सिमॉन से वीबीए उदाहरण में 'oEmbFile.Object.SaveAs fName' जैसे कुछ कॉल करने का प्रयास क्यों नहीं करते हैं: वीबीए जैसे पायथन COM इंटरफ़ेस का उपयोग करते हैं।मैं ऑब्जेक्ट एपीआई को देखने के लिए निम्न कोड लाइन का उपयोग कर सकता हूं लेकिन केवल SaveAs को सक्रिय और कॉपी जैसी चीजें नहीं हैं। messagebox.showinfo (शीर्षक = "पैकबूटस्ट्रैप", संदेश = "आइटम obj: \ n" + str (प्रकार (obj)) + str (dir (obj)) messagebox.showinfo (शीर्षक = "पैकबूटस्ट्रैप", संदेश = "_ oleobj _: \ n" + str (type (obj._oleobj_)) + str (dir (obj._oleobj_)) लेकिन ऐसा कुछ भी नहीं है जो मुझे आगे लाए "obj._oleobj_" एक "पिलडिस्पैच" ऑब्जेक्ट है । – BREMI

उत्तर

1

विंडोज अस्थायी निर्देशिका कि अस्थायी रूप से OLE ऑब्जेक्ट की फाइल स्रोत स्टोर करेगा उपयोग पर विचार करें जब एम्बेडेड कार्यपुस्तिका में इस समाधान में भौतिक फाइलों का कोई क्लिपबोर्ड उपयोग नहीं किया जाता है।

इस दृष्टिकोण के साथ

, आप वर्तमान उपयोगकर्ता का नाम निकालते हैं और अस्थायी निर्देशिका के सभी फ़ाइलों के माध्यम से पुनरावृति करने की आवश्यकता होगी: C: \ दस्तावेज़ और सेटिंग्स \ {username} \ Local Settings \ अस्थायी (मानक एक्सेल फ़ोल्डर के लिए डंप विंडोज विस्टा/7/8/10)। इसके अलावा, in साथ एक सशर्त तरह-नाम खोज प्रयोग किया जाता है मूल फ़ाइल के basename जिसमें के रूप में नंबर प्रत्यय के साथ एक से अधिक संस्करण (1), (2), (3), ... कितनी बार स्क्रिप्ट चलाने के आधार पर मौजूद हो सकता है। यहां एक रेगेक्स खोज भी आज़माएं।

अंत में, नीचे दी गई नियमितता try...except...finally ब्लॉक का उपयोग करती है ताकि त्रुटि के बावजूद एक्सेल ऑब्जेक्ट्स को स्पष्ट रूप से अस्तित्व में रखा जा सके लेकिन किसी अपवाद संदेश को आउटपुट किया जा सके। ध्यान दें कि टेक्स्ट फ़ाइल का उपयोग करके यह केवल एक विंडोज समाधान है।

import win32com.client as win32 
import os, shutil 
from tkinter import messagebox 

# (0) Setup 
dir_path = cd = os.path.dirname(os.path.abspath(__file__)) 
print(dir_path) 

try: 
    excel = win32.gencache.EnsureDispatch('Excel.Application')  
    wb = excel.Workbooks.Open(os.path.join(dir_path, "test_excel.xlsx")) 
    ws = wb.Worksheets(1) 
    objs = ws.OLEObjects() 

    # (1) Embed file 
    f = os.path.join(dir_path, "test_txt.txt")  
    name = "test_txt_ole.txt" 
    objs.Add(Filename=f, IconLabel=name).Name = 'Test' 

    # (2) Open file from temporary folder 
    ole = ws.OLEObjects(1)   
    ole.Activate() 

    # (3) Grab the recent like-named file 
    user = os.environ.get('USERNAME') 
    outfile = os.path.join(dir_path, "test_txt_out.txt") 

    tempfolder = r"C:\Documents and Settings\{}\Local Settings\Temp".format(user) 

    for subdir, dirs, files in os.walk(tempfolder): 
     for file in sorted(files, reverse=True): 
      if 'test_txt' in file:     
       tempfile = os.path.join(tempfolder, file) 
       break 

    shutil.copyfile(tempfile, outfile) 

    # (4) Read text content 
    with open(outfile, 'r') as f:   
     content = f.readlines() 

    # (5) Output message with content 
    messagebox.showinfo(title="test_txt_ole.txt", message="".join(content)) 

except Exception as e: 
    print(e) 

finally: 
    wb.Close(True)  # CLOSES AND SAVES WORKBOOK 
    excel.Quit   # QUITS EXCEL APP 

    # RELEASES COM RESOURCES 
    ws = None; wb = None; objs = None; ole = None; excel = None 

Tkinter संदेशबॉक्स

Message Output

+0

मैं आज के लिए समय से कम हूं। तो एक छोटा गूंगा सवाल है। रेखा "ओले = ws.OLEObjects (1)" ओईएल की एक temp फ़ाइल% TEMP% में बनाता है? – BREMI

+0

क्षमा करें, लेकिन जहां तक ​​मैं समझता हूं, बीआरएमआई अपनी एक्सेल फ़ाइल के अंदर ओएलई ऑब्जेक्ट्स की सामग्री को पुनर्प्राप्त करना चाहता है। आपके कोड को मूल फ़ाइलों की सामग्री मिलती है, हालांकि, उपयोगकर्ता ने उन्हें संशोधित करने पर Excel में ओएलई ऑब्जेक्ट अलग-अलग हो सकते हैं। – z32a7ul

+0

नहीं, 'ole = ws.OLEObject (1)' उस चर के लिए वर्कशीट में पहला OLEObject असाइन करता है। – Parfait

3

ठीक है, मैं Parfait के समाधान थोड़ा hackish (बुरा अर्थ में) लगता है क्योंकि

  • यह मानता है कि एक्सेल एम्बेडिंग की बचत होगी एक अस्थायी फ़ाइल के रूप में,
  • यह मानता है कि पेट इस अस्थायी फ़ाइल की ज हमेशा उपयोगकर्ता के डिफ़ॉल्ट अस्थायी पथ,
  • यह मानता है कि आप वहाँ फ़ाइलें खोलने के लिए विशेषाधिकार आ जाते है,
  • यह मानता है कि आप एक नामकरण परंपरा का उपयोग अपने वस्तुओं की पहचान करने (जैसे 'test_txt' हमेशा नाम में पाया जाता है, आप किसी ऑब्जेक्ट 'account_data' को सम्मिलित नहीं कर सकते हैं),
  • यह मानता है कि यह सम्मेलन ऑपरेटिंग सिस्टम द्वारा परेशान नहीं है (उदाहरण के लिए यह इसे 'test_tx (1) 'चरित्र लंबाई बचाने के लिए),
  • यह मानता है कि इस सम्मेलन को कंप्यूटर पर अन्य सभी कार्यक्रमों द्वारा जाना जाता है और स्वीकार किया जाता है (कोई भी' test_txt 'वाले नामों का उपयोग नहीं करेगा)।

तो, मैंने एक वैकल्पिक समाधान लिखा।

  1. .xlsx फ़ाइल (या नए XML- आधारित प्रारूप है, जो पासवर्ड से सुरक्षित नहीं है में किसी भी अन्य Office फ़ाइल) अनज़िप एक अस्थायी पथ के लिए: इस का सार thef निम्नलिखित है।

  2. '/ xxx/embeddings' के अंदर सभी .bin फ़ाइलों के माध्यम से पुनरावृति ('xxx' = 'एक्स्ट्रा लार्ज' या 'शब्द' या 'पीपीटी'), और एक शब्दकोश .bin जिसमें 'फ़ाइलों को बनाने के कुंजियों और शब्दकोशों के रूप में अस्थायी पथ चरण 3 मानों के रूप में लौट आए।

  3. .bin फ़ाइल से जानकारी निकालें (बहुत अच्छी तरह से प्रलेखित नहीं) ओले पैकर प्रारूप के अनुसार, और को एक शब्दकोश के रूप में जानकारी लौटाएं। (केवल .txt लेकिन किसी भी फ़ाइल प्रकार, जैसे .png से नहीं 'सामग्री' के रूप में कच्चे बाइनरी डेटा, पुन: प्राप्त करता)

मैं अभी भी पायथन सीख रहा हूँ, तो यह सही (कोई त्रुटि जाँच नहीं है, कोई प्रदर्शन अनुकूलन नहीं) लेकिन आप इससे विचार प्राप्त कर सकते हैं। मैंने कुछ उदाहरणों पर इसका परीक्षण किया।

import tempfile 
import os 
import shutil 
import zipfile 
import glob 
import pythoncom 
import win32com.storagecon 


def read_zipped_xml_bin_embeddings(path_zipped_xml): 
    temp_dir = tempfile.mkdtemp() 

    zip_file = zipfile.ZipFile(path_zipped_xml) 
    zip_file.extractall(temp_dir) 
    zip_file.close() 

    subdir = { 
      '.xlsx': 'xl', 
      '.xlsm': 'xl', 
      '.xltx': 'xl', 
      '.xltm': 'xl', 
      '.docx': 'word', 
      '.dotx': 'word', 
      '.docm': 'word', 
      '.dotm': 'word', 
      '.pptx': 'ppt', 
      '.pptm': 'ppt', 
      '.potx': 'ppt', 
      '.potm': 'ppt', 
     }[ os.path.splitext(path_zipped_xml)[ 1 ] ] 
    embeddings_dir = temp_dir + '\\' + subdir + '\\embeddings\\*.bin' 

    result = {} 
    for bin_file in list(glob.glob(embeddings_dir)): 
     result[ bin_file ] = bin_embedding_to_dictionary(bin_file) 

    shutil.rmtree(temp_dir) 

    return result 


def bin_embedding_to_dictionary(bin_file): 
    storage = pythoncom.StgOpenStorage(bin_file, None, win32com.storagecon.STGM_READ | win32com.storagecon.STGM_SHARE_EXCLUSIVE) 
    for stastg in storage.EnumElements(): 
     if stastg[ 0 ] == '\1Ole10Native': 
      stream = storage.OpenStream(stastg[ 0 ], None, win32com.storagecon.STGM_READ | win32com.storagecon.STGM_SHARE_EXCLUSIVE) 

      result = {} 
      result[ 'original_filename' ] = '' # original filename in ANSI starts at byte 7 and is null terminated 
      stream.Seek(6, 0) 
      while True: 
       ch = stream.Read(1) 
       if ch == '\0': 
        break 
       result[ 'original_filename' ] += ch 

      result[ 'original_filepath' ] = '' # original filepath in ANSI is next and is null terminated 
      while True: 
       ch = stream.Read(1) 
       if ch == '\0': 
        break 
       result[ 'original_filepath' ] += ch 

      stream.Seek(4, 1) # next 4 bytes is unused 

      temporary_filepath_size = 0 # size of the temporary file path in ANSI in little endian 
      temporary_filepath_size |= ord(stream.Read(1)) << 0 
      temporary_filepath_size |= ord(stream.Read(1)) << 8 
      temporary_filepath_size |= ord(stream.Read(1)) << 16 
      temporary_filepath_size |= ord(stream.Read(1)) << 24 

      result[ 'temporary_filepath' ] = stream.Read(temporary_filepath_size) # temporary file path in ANSI 

      result[ 'size' ] = 0 # size of the contents in little endian 
      result[ 'size' ] |= ord(stream.Read(1)) << 0 
      result[ 'size' ] |= ord(stream.Read(1)) << 8 
      result[ 'size' ] |= ord(stream.Read(1)) << 16 
      result[ 'size' ] |= ord(stream.Read(1)) << 24 

      result[ 'contents' ] = stream.Read(result[ 'size' ]) # contents 

      return result 

आप इसे इस तरह उपयोग कर सकते हैं:

objects = read_zipped_xml_bin_embeddings(dir_path + '\\test_excel.xlsx') 
obj = objects.values()[ 0 ] # Get first element, or iterate somehow, the keys are the temporary paths 
print('Original filename: ' + obj[ 'original_filename' ]) 
print('Original filepath: ' + obj[ 'original_filepath' ]) 
print('Original filepath: ' + obj[ 'temporary_filepath' ]) 
print('Contents: ' + obj[ 'contents' ]) 
संबंधित मुद्दे