TransferText Method Imports All Data Into One Column

cardinal2k

New member
Local time
Yesterday, 22:41
Joined
Aug 10, 2009
Messages
9
Hi all,

Has anyone seen or resolved this kind of behaviour? I'm importing semi-colon delimited files into an Access db with VBA. I'm using a schema.ini, e.g.

[apps.txt]
CharacterSet = ANSI
ColNameHeader = True
Format = Delimited( ; )
MaxScanRows=0

[EDIT: The delimiter shows up as a smiley above. In the actual schema.ini, there are no spaces]

When I use the following code,

CurrentDb().Execute (" SELECT * INTO apps FROM [Text;FMT=Delimited;HDR=Yes;Database=" _
& sInputDirectory & ";].apps.txt;")


Access successfully imports the file with each row split out into columns as per the semi-colon delimiter.

However, if I use the TransferText method,

DoCmd.TransferText acImportDelim, _
, _
"apps", _
sInputDirectory & "apps.txt", _
True

it instead imports each row into just one column, ignoring the semi-comma delimiting. I have seen MS KB 241477 but I gather that only applies to Access 2000, whereas I'm using 2003. Any help would be really useful, as I'm at wits end. The reason for preferring TransferText is that it highlights any errors in the text file such as broken rows, whereas the Text driver just assumes the file is perfect.

Thanks,

Ed
 
Last edited:
Have you got delimiters in row one to denote different column headings?
 
Yep. Here's a sample of the apps.txt file - should have included this earlier for completeness, sorry:

APPLICATION_ID;LANGUAGE;APPLICATION_NAME;CREATED_BY;CREATION_DATE;LAST_UPDATED_BY;LAST_UPDATE_DATE;LAST_UPDATE_LOGIN;DESCRIPTION;SOURCE_LANG
0;US;Application Object Library;1;12-JUN-97;2;01-SEP-04;0;;US;
1;US;System Administration;1;12-JUN-97;2;01-SEP-04;0;;US;
3;US;Application Utilities;1;12-JUN-97;2;01-SEP-04;0;;US;
50;US;Applications DBA;1;12-JUN-97;2;01-SEP-04;0;;US;
60;US;Applications Shared Technology;1;02-MAR-00;2;01-SEP-04;0;;US;
 
If you use TransferText then you need to create an import specification which you can specify during the import in the code. See here on how to create an import spec.
 
Is this an absolute rule for TransferText? Given that we are importing about 45 files in total (the above being just an example of one) it's very hard to maintain all those import specifications, especially given the clunky Access interface.

That's why we prefer to use the schema.ini which can at least be edited by things like find and replace in notepad. I see that msdn.microsoft.com/en-us/library/aa220768(office.11).aspx states you need an import spec or a schema.ini for a fixed length file, and can leave it blank for delimited files to use the default import spec, but as I recall this doesn't think semicolons are a delimiter.

I suppose the alternative is to continue using the text driver to import, but writing some kind of pre-parser to ensure the file format is valid - this seems a little like recreating the wheel.
 
So each of the files has varying numbers of fields and datatypes?

I've never used a schema.ini file but according to the documentation you gave the link for, you would need to do an import specification for a delimited file if it is not being imported properly from leaving it blank.
 
Yeah, all the files are different. I wonder if this is still a problem in Access 2007, as our organisation will be upgrading soon.
 
Yep, just checked it. Exactly the same behaviour in Access 2007 - I'm not missing anything in the above code or configuration am I?
 
Well, if you realllly wanted to get fancy, the information for import specs are stored in two hidden tables...MSysIMEXSpecs and MSysIMEXColumns. The Specs table holds information on what character to use for the field breaks, spec name and a few other details. The Columns table holds the data for the field names, and such.

In theory you could change the values of the fields for the spec, this way you wouldn't have to create 45 different specs for importing. I've never had to do things that way, so take the advice with a grain of salt...and back up before playing around with it ;)
 
Yep, just checked it. Exactly the same behaviour in Access 2007 - I'm not missing anything in the above code or configuration am I?
Only that you are not dealing with FIXED-WIDTH files so you need an Import Specification for each if you want the data to go in properly; unfortunate, but that's the case. From what I read the schema.ini ONLY works with FIXED-WIDTH files and NOT with Delimited files.
 
There seem to be three alternatives then:

1) I can reinput all the schema.ini entries as Import Specs - from looking around the forum there doesn't seem to be way to do this with VBA, so it'd have to be a manual job and then difficult to maintain. :(

2) In theory, there might be a way to get into the internals of Access to change the default delimiter to be a semi-colon, rather than comma which it is currently expecting. How to do this seems to be undocumented though. http://www.vbforums.com/showthread.php?t=318435 seems to indicate this may not even be feasible.

3) Go through each file, pre-processing each to swap semi-colons for commas,and vice versa, then once it is imported into the DB, reversing it with SQL Update statements - great for performance.
 
Last edited:
Is this an ongoing import of 45 files? or is this a one time deal to get the database up and running?

I just copied your data with the semi colons into a text file and had no problem importing the data and specifying a semi colon as the delimiter. I save the spec and was able to view the column names, widths and such via the hidden tables I mentioned above. If it's a one time import, would be quicker to just manually import the files...shouldnt take you more than a minute or two per file, tops.
 
Is this an ongoing import of 45 files? or is this a one time deal to get the database up and running?

I just copied your data with the semi colons into a text file and had no problem importing the data and specifying a semi colon as the delimiter. I save the spec and was able to view the column names, widths and such via the hidden tables I mentioned above. If it's a one time import, would be quicker to just manually import the files...shouldnt take you more than a minute or two per file, tops.

Alas it's on-going, and with occasional (but not too frequent) changes to the import specifications. We did originally use Import specs - they worked fine as you can confirm - but whenever a change was made to the file structure (e.g. a data type shared amongst a lot of the files was changed) it required a lot of work to update them all.

Hence, the desire to use a schema.ini where we could just do a find-replace on the column name and data type. However, we also want the ability of TransferText to tell us where a text file is corrupted - the Text Driver method just ignores all corruptions such as if a carriage return appears inside a field and hence mangles the import. Access seems determined to stop me having my cake and eating it.
 
You could use the Open File for Input and read each line one by one into an array using the Split() function by having the semi-colon as a delimiter. Then you can examine each individual element of the text file field by field row by row.


David
 
Hi,

Have been following this thread as I am trying to do something similar and import txt files that are pipe delimitted. I created an Import Specification but somehow it doesn't recognise that this exists when I try to use it. Why would this be? I've checked that the name of the saved specification is spelt correctly.

Code:
DoCmd.TransferText acImportDelim, "ImportBATS", "BATSshvol20090908", "Q:\Regulatory Authority\SEC\BATS Daily Volume\BATSshvol20090908.txt"

Thanks
Hay
 
Did you nominate the pipe symbol when creating your import specification?


Can you post a sample of the text file to play with?

David
 
Did you nominate the pipe symbol when creating your import specification?


Can you post a sample of the text file to play with?

David

Yes I did use the pipe symbol when creating the specification. If I run the saved specification directly form the saved Imports section it works fine. It just doesn't recognise the name of my saved specification when I try to use this in VBA.

Thanks
Hay
 

Attachments

Yes I did use the pipe symbol when creating the specification. If I run the saved specification directly form the saved Imports section it works fine. It just doesn't recognise the name of my saved specification when I try to use this in VBA.

Thanks
Hay

Hayley:

You state "if I run the saved specification from the saved Imports section" so does that mean you are using Access 2007? If so, there is a macro available to run a saved import.
 
Hayley:

You state "if I run the saved specification from the saved Imports section" so does that mean you are using Access 2007? If so, there is a macro available to run a saved import.

Thanks Bob I am using Access 2007 and I found that option and went down that route.

Hay
 
Guys, just for completeness, I'd thought I'd post my solution - I ended up writing a brute force preprocessor to make sure the file is in an acceptable format before being imported by DoCmd.TransferText :

It's a little bit scruffy - sorry about the GOTOs. Also, there are constants and functions to report errors and update the screen inline of this function. I hope it's useful to someone.

I pass in as parameters a recordset containing a list of all the files I want to read in, and also the input directory where they are held. Hopefully, this'll be some use to the other people out there


Code:
Private Function CheckFilesForCorruption(r As DAO.Recordset, _
                            sInputDirectory As String) As Boolean

  Dim sProgressBarIncrement As Single
  Dim ReadFileHandle As Integer

  Dim sTableOut As String
  Dim sFileName As String
  Dim sInputLine As String
  Dim sFieldsPerSchema() As String
  Dim sFieldsImported() As String
  Dim iFieldSizesPerSchema() As Integer
  Dim iSemiColonCount As Integer
  Dim lLineCounter As Long
  Dim iFieldCounter As Integer
    
  If PRODUCTION_RELEASE Then On Error GoTo ErrorHandler

  CheckSpoolFilesForCorruption = True
  sProgressBarIncrement = 100# / r.RecordCount 'Calculate how many steps we have
  
  UpdateProgressBar Me, pbarStepProgress, 0
  lblCurrentStep.Caption = PROCESS_MSG_CHECK_CORRUPTION
  
  With r
    .MoveFirst
    
    Do While Not .EOF
         
      'Setup variables and update the GUI.
      sTableOut = !Table_Names
      sFileName = !Import_File_Name
      ReDim sFieldsPerSchema(0)
      ReDim iFieldSizesPerSchema(0)
      UpdateProgressBar Me, pbarStepProgress, _
                        pbarStepProgress.Value + sProgressBarIncrement
                        
      'First parse the schema.ini to find out what the file should look like
      ReadFileHandle = FreeFile
      Open sInputDirectory & "schema.ini" For Input As #ReadFileHandle
      
      Do While Not EOF(ReadFileHandle)
      
        'Search the schema.ini for the field definitions
        Line Input #ReadFileHandle, sInputLine
        
        'Find the entry for the file
        If Mid(sInputLine, 1, Len(sFileName) + 2) = _
              "[" & sFileName & "]" Then
              
          'Read in the field information
          Do While Not EOF(ReadFileHandle) And sInputLine <> ""
              
            Line Input #ReadFileHandle, sInputLine
              
            If Left(sInputLine, 4) Like "Col#" Then
              ReDim Preserve sFieldsPerSchema(UBound(sFieldsPerSchema) + 1)
              ReDim Preserve iFieldSizesPerSchema(UBound(sFieldsPerSchema) + 1)
              
              'Set the Upper element 1st part on the sFieldsPerSchema array to be the column
              'name per the Schema.ini file.
              sFieldsPerSchema(UBound(sFieldsPerSchema) - 1) = _
                  Mid(sInputLine, _
                      InStr(1, sInputLine, "=") + 1, _
                      InStr(1, sInputLine, " ") - InStr(1, sInputLine, "=") - 1)
              'Set the Upper element 2nd part on the sFieldsPerSchema array to be the text width
              'if applicable per the Schema.ini file.
              If Right(sInputLine, 2) Like "##" Then
                iFieldSizesPerSchema(UBound(sFieldsPerSchema) - 1) = _
                    CInt(Mid(sInputLine, InStrRev(sInputLine, " ")))
              End If
            End If
          Loop
          
        ReDim Preserve sFieldsPerSchema(UBound(sFieldsPerSchema) - 1)
        ReDim Preserve iFieldSizesPerSchema(UBound(iFieldSizesPerSchema) - 1)
        GoTo CloseSchemeIni
        End If
      Loop
CloseSchemeIni:
      Close #ReadFileHandle
      
      'We are done with the Schema.ini file. We should have, after all that, a sFieldsPerSchema
      'array containing all the column names per the Schema.ini file.
                        
      'Now open up the Spool file.
      
      'We want to check
      ' 1) Are the column headings the same (for text description, and in number)
      ' 2) For each data row, are there enough fields per line
      ' 3) Are the fields in the row greater than 255 characters
      ' 4) Are there blank lines in the file?
      ' 5) Are there " marks in the file
      
      ReadFileHandle = FreeFile
      
      Open sInputDirectory & sFileName For Input As #ReadFileHandle
      
      If LOF(ReadFileHandle) = 0 Then
        CurrentDb.Execute SQL_APPEND_TO_ERROR_TABLE(sTableOut, _
                                                    sFileName, _
                                                    lLineCounter, _
                                                    True, _
                                                    ERROR_MSG_CORRUPTION_FILE_EMPTY)
        GoTo TryNextFile
      End If
      
      'Check the headings line
      Line Input #ReadFileHandle, sInputLine
      lLineCounter = 1
      sFieldsImported = Split(sInputLine, ";")
      iSemiColonCount = UBound(sFieldsImported)
      
      ' 1) Check there is the same number
      If UBound(sFieldsImported) <> UBound(sFieldsPerSchema) Then
      
        CurrentDb.Execute SQL_APPEND_TO_ERROR_TABLE(sTableOut, _
                                                    sFileName, _
                                                    lLineCounter, _
                                                    True, _
                                                    ERROR_MSG_CORRUPTION_FIELD_COUNT_UNEQUAL)
        CheckSpoolFilesForCorruption = False
        GoTo TryNextFile
      Else
        'If there is the same number, check they have the same names
        For iFieldCounter = 0 To UBound(sFieldsPerSchema)
          If sFieldsImported(iFieldCounter) <> sFieldsPerSchema(iFieldCounter) Then
            CurrentDb.Execute SQL_APPEND_TO_ERROR_TABLE(sTableOut, _
                                                        sFileName, _
                                                        lLineCounter, _
                                                        False, _
                                                        ERROR_CORRUPTION_MSG_FIELD_NAMES_UNEQUAL)
          End If
        Next iFieldCounter
      End If
            
TryNextLine:
      Do While Not EOF(ReadFileHandle)
      
        Line Input #ReadFileHandle, sInputLine
        lLineCounter = lLineCounter + 1
        'Note, the header line has no trailing ; but data lines do. Hence, we sort this out below.
        sFieldsImported = Split(sInputLine, ";")
      
        'Update the screen every 1000 lines
        If lLineCounter Mod 1000 = 0 Then
          lblCurrentStep.Caption = PROCESS_MSG_CHECK_CORRUPTION & sTableOut & _
                                    " Line: " & Format(lLineCounter, "#,###")
          Me.Repaint
        End If
      
        'Check blank lines
        If sInputLine = "" Then
          CurrentDb.Execute SQL_APPEND_TO_ERROR_TABLE(sTableOut, _
                                                      sFileName, _
                                                      lLineCounter, _
                                                      False, _
                                                      ERROR_MSG_CORRUPTION_BLANK_LINE)
          GoTo TryNextLine
        End If
        
        'Check data field count
        If iSemiColonCount <> Maximum(UBound(sFieldsImported) - 1, 0) Then
          CurrentDb.Execute SQL_APPEND_TO_ERROR_TABLE(sTableOut, _
                                                      sFileName, _
                                                      lLineCounter, _
                                                      True, _
                                                      ERROR_MSG_CORRUPTION_MALFORMED_DATA_LINE)
          GoTo TryNextLine
        End If
      
        'Check each data item:
        For iFieldCounter = 0 To UBound(sFieldsImported)
          'For length ...
          If iFieldSizesPerSchema(iFieldCounter) > 0 And _
              Len(sFieldsImported(iFieldCounter)) > iFieldSizesPerSchema(iFieldCounter) Then
              CurrentDb.Execute SQL_APPEND_TO_ERROR_TABLE(sTableOut, _
                                                          sFileName, _
                                                          lLineCounter, _
                                                          False, _
                                                          ERROR_CORRUPTION_MSG_FIELD_TOO_LONG)
          End If
          '... and illegal characters
          If InStr(1, sFieldsImported(iFieldCounter), QUOTE_MARK) <> 0 Then
            CurrentDb.Execute SQL_APPEND_TO_ERROR_TABLE(sTableOut, _
                                                        sFileName, _
                                                        lLineCounter, _
                                                        True, _
                                                        ERROR_CORRUPTION_MSG_FIELD_CONTAINS_QUOTE_MARK)
          End If
        Next iFieldCounter
      
      Loop
      
TryNextFile:
      Close #ReadFileHandle
      .MoveNext
    Loop
  End With

Exit Function

ErrorHandler:
  MsgBox ERROR_MSG_CHECK_FILES_FOR_CORRUPTION & Err.Description


End Function
 

Users who are viewing this thread

Back
Top Bottom