如何以编程方式猜测CSV文件是以逗号分隔还是以分号分隔

时间:2010-05-07 15:28:42

标签: parsing csv delimiter

在大多数情况下,CSV文件是包含以逗号分隔的记录的文本文件。但是,有时这些文件将以分号分隔。 (如果区域设置将小数分隔符设置为逗号,Excel将在保存CSV时使用分号分隔符 - 这在欧洲很常见。参考:http://en.wikipedia.org/wiki/Comma-separated_values#Application_support

我的问题是,让程序猜测是用逗号分号还是以分号分隔的最佳方法是什么?

e.g。像1,1; 1,1这样的线可能不明确。它可以解释为逗号分隔为: 1 1; 1(一个字符串) 1

或分号分隔为 1,1 1,1

到目前为止,我最好的猜测是尝试使用和解析文件;分隔符,然后选择具有与第一行相同长度的行的解析(通常是标题行)。如果两者具有相同的行数,请选择具有更多列的行。这样做的主要缺点是额外的开销。

思想?

5 个答案:

答案 0 :(得分:1)

根据您的工作情况,如果您保证有标题行,那么您尝试两者的方法可能是最好的整体练习。然后,一旦确定发生了什么,如果你进一步向下进入没有所需列数的行,那么你就知道格式不正确。

通常我会在上传时将其视为用户指定的选项,而不是程序化测试。

答案 1 :(得分:1)

如果每一行都应该具有相同的列数,我相信Excel就是这种情况,那么,使用逗号和分号,计算出行N和N + 1的列数。无论哪种方法(逗号或分号)产生不同的答案都是错误的(不是文件的格式)。你可以从头开始,你只需要去其中一个被证明不正确。您不需要标题行或任何内容。你没有必要阅读更多的文件,它不能给你一个错误的文件格式的答案,它可能会达到目的,还没有得出结论。您只需要为每一行保留相同数量的列属性。

答案 2 :(得分:1)

您可以阅读第一行

FileReader fileReader = new FileReader(filePath);
    BufferedReader bufferedReader = new BufferedReader(fileReader);
    String s = bufferedReader.readLine();
    String substring = s.substring(s.indexOf(firstColumnName) + 3, s.indexOf(firstColumnName) + 4);
    bufferedReader.close();
    fileReader.close();
    substring.charAt(0);

然后你捕获这个值

  

substring.charAt(0)

取决于CSV是逗号还是分号可以使用最后一个值

答案 3 :(得分:0)

这是我的代码(没有对文本进行验证)......也许它可以帮助或建立基础:-)!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using MoreLinq; // http://stackoverflow.com/questions/15265588/how-to-find-item-with-max-value-using-linq

namespace HQ.Util.General.CSV
{
    public class CsvHelper
    {
        public static Dictionary<LineSeparator, Func<string, string[]>>  DictionaryOfLineSeparatorAndItsFunc = new Dictionary<LineSeparator, Func<string, string[]>>();

        static CsvHelper()
        {
            DictionaryOfLineSeparatorAndItsFunc[LineSeparator.Unknown] = ParseLineNotSeparated;
            DictionaryOfLineSeparatorAndItsFunc[LineSeparator.Tab] = ParseLineTabSeparated;
            DictionaryOfLineSeparatorAndItsFunc[LineSeparator.Semicolon] = ParseLineSemicolonSeparated;
            DictionaryOfLineSeparatorAndItsFunc[LineSeparator.Comma] = ParseLineCommaSeparated;
        }

        // ******************************************************************
        public enum LineSeparator
        {
            Unknown = 0,
            Tab,
            Semicolon,
            Comma
        }

        // ******************************************************************
        public static LineSeparator GuessCsvSeparator(string oneLine)
        {
            List<Tuple<LineSeparator, int>> listOfLineSeparatorAndThereFirstLineSeparatedValueCount = new List<Tuple<LineSeparator, int>>();

            listOfLineSeparatorAndThereFirstLineSeparatedValueCount.Add(new Tuple<LineSeparator, int>(LineSeparator.Tab, CsvHelper.ParseLineTabSeparated(oneLine).Count()));
            listOfLineSeparatorAndThereFirstLineSeparatedValueCount.Add(new Tuple<LineSeparator, int>(LineSeparator.Semicolon, CsvHelper.ParseLineSemicolonSeparated(oneLine).Count()));
            listOfLineSeparatorAndThereFirstLineSeparatedValueCount.Add(new Tuple<LineSeparator, int>(LineSeparator.Comma, CsvHelper.ParseLineCommaSeparated(oneLine).Count()));

            Tuple<LineSeparator, int> bestBet = listOfLineSeparatorAndThereFirstLineSeparatedValueCount.MaxBy((n)=>n.Item2);

            if (bestBet != null && bestBet.Item2 > 1)
            {
                return bestBet.Item1;
            }

            return LineSeparator.Unknown;
        }

        // ******************************************************************
        public static string[] ParseLineCommaSeparated(string line)
        {
            // CSV line parsing : From "jgr4" in http://www.kimgentes.com/worshiptech-web-tools-page/2008/10/14/regex-pattern-for-parsing-csv-files-with-embedded-commas-dou.html
            var matches = Regex.Matches(line, @"\s?((?<x>(?=[,]+))|""(?<x>([^""]|"""")+)""|""(?<x>)""|(?<x>[^,]+)),?",
                                        RegexOptions.ExplicitCapture);

            string[] values = (from Match m in matches
                               select m.Groups["x"].Value.Trim().Replace("\"\"", "\"")).ToArray();

            return values;
        }

        // ******************************************************************
        public static string[] ParseLineTabSeparated(string line)
        {
            var matchesTab = Regex.Matches(line, @"\s?((?<x>(?=[\t]+))|""(?<x>([^""]|"""")+)""|""(?<x>)""|(?<x>[^\t]+))\t?",
                            RegexOptions.ExplicitCapture);

            string[] values = (from Match m in matchesTab
                                select m.Groups["x"].Value.Trim().Replace("\"\"", "\"")).ToArray();

            return values;
        }

        // ******************************************************************
        public static string[] ParseLineSemicolonSeparated(string line)
        {
            // CSV line parsing : From "jgr4" in http://www.kimgentes.com/worshiptech-web-tools-page/2008/10/14/regex-pattern-for-parsing-csv-files-with-embedded-commas-dou.html
            var matches = Regex.Matches(line, @"\s?((?<x>(?=[;]+))|""(?<x>([^""]|"""")+)""|""(?<x>)""|(?<x>[^;]+));?",
                                        RegexOptions.ExplicitCapture);

            string[] values = (from Match m in matches
                               select m.Groups["x"].Value.Trim().Replace("\"\"", "\"")).ToArray();

            return values;
        }

        // ******************************************************************
        public static string[] ParseLineNotSeparated(string line)
        {
            string [] lineValues = new string[1];
            lineValues[0] = line;
            return lineValues;
        }

        // ******************************************************************
        public static List<string[]> ParseText(string text)
        {
            string[] lines = text.Split(new string[] { "\r\n" }, StringSplitOptions.None);
            return ParseString(lines);
        }

        // ******************************************************************
        public static List<string[]> ParseString(string[] lines)
        {
            List<string[]> result = new List<string[]>();

            LineSeparator lineSeparator = LineSeparator.Unknown;
            if (lines.Any())
            {
                lineSeparator = GuessCsvSeparator(lines[0]);
            }

            Func<string, string[]> funcParse = DictionaryOfLineSeparatorAndItsFunc[lineSeparator];

            foreach (string line in lines)
            {
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }

                result.Add(funcParse(line));
            }

            return result;
        }

        // ******************************************************************
    }
}

答案 4 :(得分:0)

假设您的 csv 中有以下内容:

<!-- desserts.php -->
<!-- ##### New Recipe Area Start.  This section displays all recipes from the database. ##### -->
    <section class="small-receipe-area section-padding-80-0">

        <div class="section-heading">
                        <h3>Dessert Recipes</h3>
        </div>
        <div class="container">
            <div class="row">

                <?php
                    while ($row = mysqli_fetch_assoc($result)){
                    list_of_recipes($row['recipe_name'], $row['recipe_img'], $row['recipe_link'], $row['stars'], $row['date_entered']);
                }
                ?>

            </div>
        </div>
    </section>
    <!-- ##### New Recipe Area End ##### -->



<!-- recipe_list_component.php -->
<!-- ##### This function is called by desserts.php for every recipe found in the database in this category. ##### -->
<?php

function list_of_recipes($recipename, $recipeimg, $recipelnk, $stars, $recipedate){
$i = "";
$element = "

    <!-- New Recipe Area -->
                <div class=\"col-12 col-sm-6 col-lg-4\">
                    <div class=\"single-small-receipe-area d-flex\">
                        <!-- Recipe Thumb -->
                        <div class=\"receipe-thumb\">
                        <img src=\"$recipeimg\" alt=\"caramel_sauce\">
                        <!--<img src=\"img/bg-img/caramel1_thumb.jpg\" alt=\"\">-->
                        </div>
                        <!-- Recipe Content -->
                        <div class=\"receipe-content\">
                            <span>$recipedate</span>
                            <a href=\"$recipelnk\">
                                <h5>$recipename</h5>
                            </a>
                            <div class=\"ratings\">
                                <i class=\"fa fa-star\" aria-hidden=\"true\"></i>
                                <i class=\"fa fa-star\" aria-hidden=\"true\"></i>
                                <i class=\"fa fa-star\" aria-hidden=\"true\"></i>
                                <i class=\"fa fa-star\" aria-hidden=\"true\"></i>
                                <i class=\"fa fa-star\" aria-hidden=\"true\"></i>

                            </div>
                            <p>0 Comments</p>
                        </div>
                    </div>
                </div>
        ";

        echo $element;

}

?>

然后你可以使用python的内置CSV模块如下:

title,url,date,copyright,hdurl,explanation,media_type,service_version

打印名为 import csv data = "title,url,date,copyright,hdurl,explanation,media_type,service_version" sn = csv.Sniffer() delimiter = sn.sniff(data).delimiter 的变量将返回 delimiter,这是此处的分隔符。您可以使用一些不同的分隔符进行测试。