python字典写入csv - python讀取csv




处理CSV文件中的逗号 (14)

CSV格式使用逗号分隔值,包含回车符,换行符,逗号或双引号的值用双引号括起来。 包含双引号的值被引用,并且每个文字引号都被前面的引号转义:例如,3个值:

test
list, of, items
"go" he said

将被编码为:

test
"list, of, items"
"""go"" he said"

任何字段都可以被引用,但只有包含逗号,CR / NL或引号的字段必须被引用。

CSV格式没有真正的标准 ,但几乎所有的应用程序都遵循here记录的约定。 其他地方提到的RFC不是CSV的标准,它是在MIME中使用CSV的RFC,并且包含一些非常规和不必要的限制,使其在MIME之外无用。

我见过的很多CSV模块都无法容纳的一个问题是,多行可以编码在单个字段中,这意味着您不能假定每行都是单独的记录,您可能不需要在您的行中允许换行数据或准备处理这个。

我正在寻找关于如何处理正在创建,然后由我们的客户上传的csv文件的建议,并且该文件可能具有逗号(例如公司名称)。

我们所看到的一些想法是:引用标识符(值“,”值“,”等)或使用| 而不是逗号。 最大的问题是我们必须简化,否则客户不会这样做。


你可以像这样读取csv文件。

这使用分裂和照顾空间。

ArrayList List = new ArrayList();
static ServerSocket Server;
static Socket socket;
static ArrayList<Object> list = new ArrayList<Object>();


public static void ReadFromXcel() throws FileNotFoundException
{   
    File f = new File("Book.csv");
    Scanner in = new Scanner(f);
    int count  =0;
    String[] date;
    String[] name;
    String[] Temp = new String[10];
    String[] Temp2 = new String[10];
    String[] numbers;
    ArrayList<String[]> List = new ArrayList<String[]>();
    HashMap m = new HashMap();

         in.nextLine();
         date = in.nextLine().split(",");
         name = in.nextLine().split(",");
         numbers = in.nextLine().split(",");
         while(in.hasNext())
         {
             String[] one = in.nextLine().split(",");
             List.add(one);
         }
         int xount = 0;
         //Making sure the lines don't start with a blank
         for(int y = 0; y<= date.length-1; y++)
         {
             if(!date[y].equals(""))
             {   
                 Temp[xount] = date[y];
                 Temp2[xount] = name[y];
                 xount++;
             }
         }

         date = Temp;
         name =Temp2;
         int counter = 0;
         while(counter < List.size())
         {
             String[] list = List.get(counter);
             String sNo = list[0];
             String Surname = list[1];
             String Name = list[2];
             for(int x = 3; x < list.length; x++)
             {           
                 m.put(numbers[x], list[x]);
             }
            Object newOne = new newOne(sNo, Name, Surname, m, false);
             StudentList.add(s);
             System.out.println(s.sNo);
             counter++;
         }

使用制表符(\ t)分隔字段。


在字符串周围放置双引号。 这通常here

Ala Eli,

你将双引号转义为两个双引号。 例如“test1”,“foo”,“bar”,“test2”


如果您使用的是* nix系统 ,有权访问sed并且只能在 CSV 特定字段中使用一个或多个不需要的逗号 ,则可以使用以下单行命令将它们括在" as RFC4180第2节提出:

sed -r 's/([^,]*,[^,]*,[^,]*,)(.*)(,.*,.*)/\1"\2"\3/' inputfile

根据不需要的逗号可能在哪个字段中,您必须更改/扩展正则表达式的捕获组(以及替换)。
上面的例子将引号中的第四个字段(六个中)。

结合“ --in-place-option您可以将这些更改直接应用于文件。

为了“构建”正确的正则表达式,有一个简单的原则可以遵循:

  1. 对于您的CSV中带有不需要的逗号的字段前面的每个字段,您都会写一个[^,]*,并将它们放在一个捕获组中。
  2. 对于包含您写入的不需要的逗号的字段(.*)
  3. 对于有多余逗号的字段后面的每个字段您都会写一个,.* ,并将它们放在一个捕获组中。

以下是根据特定领域的不同可能的正则表达式/替换的简要概述。 如果没有给出,替代是\1"\2"\3

([^,]*)(,.*)                     #first field, regex
"\1"\2                           #first field, substitution

(.*,)([^,]*)                     #last field, regex
\1"\2"                           #last field, substitution


([^,]*,)(.*)(,.*,.*,.*)          #second field (out of five fields)
([^,]*,[^,]*,)(.*)(,.*)          #third field (out of four fields)
([^,]*,[^,]*,[^,]*,)(.*)(,.*,.*) #fourth field (out of six fields)

如果您想用sed删除不需要的逗号(而不是用引号括起来),请参考此答案 。


如果您想重新发明轮子,以下内容可能适合您:

public static IEnumerable<string> SplitCSV(string line)
{
    var s = new StringBuilder();
    bool escaped = false, inQuotes = false;
    foreach (char c in line)
    {
        if (c == ',' && !inQuotes)
        {
            yield return s.ToString();
            s.Clear();
        }
        else if (c == '\\' && !escaped)
        {
            escaped = true;
        }
        else if (c == '"' && !escaped)
        {
            inQuotes = !inQuotes;
        }
        else
        {
            escaped = false;
            s.Append(c);
        }
    }
    yield return s.ToString();
}

对于2017年,csv完全指定 - RFC 4180。

这是一个非常常见的规范,并且被许多库( github.com/Flinesoft/CSVImporter )完全覆盖。

只需使用任何容易获得的csv库 - 也就是说RFC 4180。

实际上有一个CSV格式的规范以及如何处理逗号:

包含换行符(CRLF),双引号和逗号的字段应用双引号引起来。

http://tools.ietf.org/html/rfc4180

所以,要bar,baz foobar,baz ,你这样做:

foo,"bar,baz"

另一个需要考虑的重要要求(也来自规范):

如果使用双引号将字段括起来,那么出现在字段内的双引号必须通过在另一个双引号之前进行转义来进行转义。 例如:

"aaa","b""bb","ccc"


我认为解决这个问题最简单的方法是让客户在excel中打开csv,然后按Ctrl + r将所有逗号替换为您想要的任何标识符。 这对客户来说非常简单,只需要对代码进行一次更改就可以读取您选择的分隔符。


我通常在我的CSV文件解析例程中执行此操作。 假设“行”变量是CSV文件中的一行,并且所有列的值都用双引号引起来。 在执行以下两行后,您将在'values'集合中获得CSV列。

// The below two lines will split the columns as well as trim the DBOULE QUOTES around values but NOT within them
    string trimmedLine = line.Trim(new char[] { '\"' });
    List<string> values = trimmedLine.Split(new string[] { "\",\"" }, StringSplitOptions.None).ToList();

有一个库可通过nuget处理几乎任何格式良好的CSV(.net) - CsvHelper

映射到类的示例:

var csv = new CsvReader( textReader );
var records = csv.GetRecords<MyClass>();

读取单个字段的示例:

var csv = new CsvReader( textReader );
while( csv.Read() )
{
    var intField = csv.GetField<int>( 0 );
    var stringField = csv.GetField<string>( 1 );
    var boolField = csv.GetField<bool>( "HeaderName" );
}

让客户端驱动文件格式:
,是标准字段分隔符"是用于转义包含分隔符,引号或行结尾的字段的标准值。

要使用(例如) #作为字段和'转义:

var csv = new CsvReader( textReader );
csv.Configuration.Delimiter = "#";
csv.Configuration.Quote = ''';
// read the file however meets your needs

更多文档


正如其他人所说的,您需要避开包含引号的值。 这里有一个支持引用值的C♯CSV阅读器,包括嵌入式引号和回车符。

顺便说一下,这是经过单元测试的代码。 我现在发布它,因为这个问题似乎出现了很多,而其他人在简单的CSV支持时可能不需要整个图书馆。

你可以使用它如下:

using System;
public class test
{
    public static void Main()
    {
        using ( CsvReader reader = new CsvReader( "data.csv" ) )
        {
            foreach( string[] values in reader.RowEnumerator )
            {
                Console.WriteLine( "Row {0} has {1} values.", reader.RowIndex, values.Length );
            }
        }
        Console.ReadLine();
    }
}

这里是类。 请注意,您也可以使用Csv.Escape函数编写有效的CSV。

using System.IO;
using System.Text.RegularExpressions;

public sealed class CsvReader : System.IDisposable
{
    public CsvReader( string fileName ) : this( new FileStream( fileName, FileMode.Open, FileAccess.Read ) )
    {
    }

    public CsvReader( Stream stream )
    {
        __reader = new StreamReader( stream );
    }

    public System.Collections.IEnumerable RowEnumerator
    {
        get {
            if ( null == __reader )
                throw new System.ApplicationException( "I can't start reading without CSV input." );

            __rowno = 0;
            string sLine;
            string sNextLine;

            while ( null != ( sLine = __reader.ReadLine() ) )
            {
                while ( rexRunOnLine.IsMatch( sLine ) && null != ( sNextLine = __reader.ReadLine() ) )
                    sLine += "\n" + sNextLine;

                __rowno++;
                string[] values = rexCsvSplitter.Split( sLine );

                for ( int i = 0; i < values.Length; i++ )
                    values[i] = Csv.Unescape( values[i] );

                yield return values;
            }

            __reader.Close();
        }
    }

    public long RowIndex { get { return __rowno; } }

    public void Dispose()
    {
        if ( null != __reader ) __reader.Dispose();
    }

    //============================================


    private long __rowno = 0;
    private TextReader __reader;
    private static Regex rexCsvSplitter = new Regex( @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))" );
    private static Regex rexRunOnLine = new Regex( @"^[^""]*(?:""[^""]*""[^""]*)*""[^""]*$" );
}

public static class Csv
{
    public static string Escape( string s )
    {
        if ( s.Contains( QUOTE ) )
            s = s.Replace( QUOTE, ESCAPED_QUOTE );

        if ( s.IndexOfAny( CHARACTERS_THAT_MUST_BE_QUOTED ) > -1 )
            s = QUOTE + s + QUOTE;

        return s;
    }

    public static string Unescape( string s )
    {
        if ( s.StartsWith( QUOTE ) && s.EndsWith( QUOTE ) )
        {
            s = s.Substring( 1, s.Length - 2 );

            if ( s.Contains( ESCAPED_QUOTE ) )
                s = s.Replace( ESCAPED_QUOTE, QUOTE );
        }

        return s;
    }


    private const string QUOTE = "\"";
    private const string ESCAPED_QUOTE = "\"\"";
    private static char[] CHARACTERS_THAT_MUST_BE_QUOTED = { ',', '"', '\n' };
}

添加对Microsoft.VisualBasic的引用(是的,它说的是VisualBasic,但它也适用于C# - 记住最后它只是IL)。

使用Microsoft.VisualBasic.FileIO.TextFieldParser类来解析CSV文件以下是示例代码:

 Dim parser As TextFieldParser = New TextFieldParser("C:\mar0112.csv")
 parser.TextFieldType = FieldType.Delimited
 parser.SetDelimiters(",")      

   While Not parser.EndOfData         
      'Processing row             
      Dim fields() As String = parser.ReadFields         
      For Each field As String In fields             
         'TODO: Process field                   

      Next      
      parser.Close()
   End While 

由于这是关于一般做法,我们从拇指规则开始:

  1. 不要使用CSV,将XML与库一起使用来读取和写入xml文件。

  2. 如果您必须使用CSV。 正确使用并使用免费库来解析和存储CSV文件。

为了证明1),大多数CSV解析器都不能识别编码,所以如果你没有处理US-ASCII,那么你需要寻找麻烦。 例如,excel 2002以本地编码方式存储CSV,而没有任何关于编码的注释。 CSV标准没有被广泛采用:(另一方面,xml标准被很好地采用并且它处理编码相当好。

为了证明2),几乎所有的语言都有大量的csv解析器,所以即使解决方案看起来很简单,也不需要重新发明轮子。

仅举几例:

  • python使用csv模块构建

  • perl检查CPAN和Text::CSV

  • 为PHP使用建立在fgetcsv / fputcsv函数

  • 为java检查SuperCVS

如果你不打算在嵌入式设备上解析它,真的不需要手动实现。





csv