I’ve run into this issue in the past, and also got an email about this issue this past week as well, so I figured it’s worth taking a look. Power Query takes certain liberties when importing a file, assuming it knows what type of file it is. The problem is that sometimes this doesn’t work as expected, and you need to be able to force Power Query to import as a text file, not the file format that Power Query assumes you have.
IT standards are generally a beautiful thing, especially in programming, as you can rely on them, knowing that certain rules will always be followed. CSV files are a prime example of this, and we should be able to assume that any CSV file will contain a list of Comma Separated Values, one record per line, followed by a new line character. Awesome… until some bright spark decides to inject a line or two of information above the CSV contents which doesn’t contain any commas. (If you do that, please stop. It is NOT A GOOD IDEA.)
The Issue in the Real World
If you’d like to follow along, you can click here to download MalformedCSV.csv (the sample file).
If you open the sample file in Notepad, you’ll see that it contains the following rows:
Notice the first row… that’s where our issue is. There are no commas. Yet when you look at the data in the rows below, they are plainly separated by commas. Well yay, so what, right? Who cares about the guts of a CSV file? The answer is “you” if you ever get one that is built like this…
Let’s try importing the sample file into Power Query:
- Power Query –> From File –> From CSV
- Browse to MalformedCSV.csv
And the result is as follows:
One header, and lots of errors. Not so good!
The Source Of The Error
If I click the white space beside one of those errors, I get this:
What the heck does that even mean?
Remember that CSV is a standard. Every comma indicates a column break, every carriage return a new line. And we also know that every CSV file has a consistent number of columns (and therefore commas) on every row. (That’s why you’ll see records in some CSV’s that read ,, – because there isn’t anything for that record, but we still need the same number of commas to denote the columns.
And now some joker builds us a file masquerading as a CSV that really isn’t. In this case:
- Our first row has no commas before the line feed. We therefore must have a one column table. Power Query sets us up for a one column table.
- But our second row has three commas, which means three columns… That’s not the same number of columns as our header row, so Power Query freaks out and throws an error for every subsequent record.
So Now What?
If we can’t rely on the file format being correct, why don’t we just import it as a text file? That would allow us to bring all the rows in, remove the first one, then split by commas. That sounds like a good plan. Let’s do it.
- Power Query –> From File –> From Text
- Browse to the folder that holds MalformedCSV.csv
Uh oh… our file is not showing. Ah… we’re filtered to only show *.txt and *.prn files…
- Click the file filter list in the bottom right and change “Text File (*.txt;*.prn)” to “All Files (*.*)”
- Open MalformedCSV.csv
And the result…
Damn. See, Power Query is too smart. It looks at the file and says “Hey! That’s not a text file, it’s a CSV file!” and then imports it as a CSV file... which we already know has issues. Grr…
Force Power Query to Import as a Text File
Let’s try this again, this time from scratch. We need two things here…
- The full file path to the file you want to import (including the file extension). On my system it is "D:\Test\MalformedCSV.csv"
- A little bit of a code template, which is conveniently included below.
What we’re going to do is this:
- Go to Power Query –> From Other Sources –> Blank Query
- View –> Advanced Editor
- Paste in the following code
/* Get the raw line by line contents of the file, preventing PQ from interpreting it */
fnRawFileContents = (fullpath as text) as table =>
Value = Table.FromList(Lines.FromBinary(File.Contents(fullpath)),Splitter.SplitByNothing())
/* Use function to load file contents */
Source = fnRawFileContents("D:\Test\MalformedCSV.csv")
- Update the file path in the “Source” step to the path to your file.
- Click Done
And the output is remarkably different… in fact, it’s the entire contents of the file!
This is awesome, as we now have the ability to clean up our data and use it as we were hoping to do from the beginning! So let’s do just that…starting with killing off that first line that’s been screwing us up:
- Home –> Remove Rows –> Remove Top Rows –> 1
- Transform –> Split Column By Delimiter –> Comma –> At each occurrence of the delimiter
- Transform –> User First Row As Headers
And now we’ve got a pretty nice table of data that actually looks like our CSV file:
The final steps to wrap this up would essentially be
- set our data types,
- name the query. and
- load the query to the final destination.
This seems too hard. I know that Power Query is designed for ease of use, and to that end it’s great that it can and does make assumptions about your file. Most of the time it gets it exactly right and there is no issue. But when things do go wrong it's really hard to get back to a raw import format so that we can take control.
I really think there should be an easy and discoverable way to do a raw import of data without making import/formatting assumptions. A button for “Raw Text Import” would be a useful addition for those scenarios where stuff goes wrong and needs a careful hand.
I should also mention that this function will work on txt files or prn files as well. In fact, it also works on Excel files to some degree, although the results aren’t exactly useful!
The key here is, whether caused by one of more extra header lines in a csv file, tab delimited, colon delimited or any other kind of delimited file, the small function template above will help you get past that dreaded message that reads “DataFormat.Error: There were more columns in the result than expected.”
Addendum To This Post
Miguel Escobar has recorded a nice video of the way to do this without using any code. You can find that here: