DigitalPlatform / dp2

Integrated Library System / 图书馆集成系统
http://digitalplatform.github.io/dp2
Apache License 2.0
105 stars 54 forks source link

常用的MarcQuery脚本集锦 #287

Open lunvo opened 5 years ago

lunvo commented 5 years ago

MarcQuery 脚本使用方法 https://github.com/DigitalPlatform/dp2/wiki/MarcQuery-脚本使用方法

MarcQuery 函数库的参考手册:http://dp2003.com/marcquery/

如何在 dp2catalog 中执行 MarcQuery 脚本? https://github.com/DigitalPlatform/dp2/issues/344

renyh commented 5 years ago

清空 606$a 子字段内容(dp2circulation)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;
using dp2Circulation;
using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;
public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        // 先检索出606$a子字段
        MarcNodeList subfields = this.MarcRecord.select("field[@name='606']/subfield[@name='a']");
        foreach (MarcNode node in subfields)
        {
            node.Content = ""; // 将606$a子字段内容设为空
            this.Changed = true;  //将这条记录标识修改状态,这样后面可以保存修改。
        }
    }
}
DigitalPlatform commented 5 years ago

删除 606$a 子字段

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        MarcNodeList subfields = this.MarcRecord.select("field[@name='606']/subfield[@name='a']");
        foreach (MarcNode node in subfields)
        {
            node.detach();   // 删除子字段
            this.Changed = true;
        }
    }
}
DigitalPlatform commented 5 years ago

新增一个 801 字段

下面代码可以给 MARC 记录无条件新增一个 801 字段:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
    this.MarcRecord.ChildNodes.insertSequence(new MarcField('$', "801  $aCN$bNLC"));
    this.Changed = true;
    }
}

而如果是当 801 字段存在的时候修改它,不存在的时候新增一个 801,可以这样写:

    public override void OnRecord(object sender, StatisEventArgs e)
    {
    MarcNodeList fields = this.MarcRecord.select("field[@name='801']");
    if (fields.count >0)
    {
        MarcNodeList subfields = fields[0].select("subfield[@name='a']");
        if (subfields.count > 0)
            subfields.Content = "CN";
        else
            fields[0].ChildNodes.insertSequence(new MarcSubfield("a", "CN"));

        subfields = fields[0].select("subfield[@name='b']");
        if (subfields.count > 0)
            subfields.Content = "NLC";
        else
            fields[0].ChildNodes.insertSequence(new MarcSubfield("b", "NLC"));
    }
    else
        this.MarcRecord.ChildNodes.insertSequence(new MarcField('$', "801  $aCN$bNLC"));

    this.Changed = true;
    }

这段代码比较复杂。

考虑到这种“有了一个字段(子字段)就修改它,没有就新增一个”的需求特别常见,MarcQuery 甚至提供了一个专门的 setFirstField() 或者 setFirstSubfield() 函数: 注:这一段脚本暴露了当前 MarcQuery 函数库一个 bug

    public override void OnRecord(object sender, StatisEventArgs e)
    {
    this.MarcRecord.setFirstSubfield("801", "a", "CN", "  ");
    this.MarcRecord.setFirstSubfield("801", "b", "NLC", "  ");

    this.Changed = true;
    }

(MarcRecord类)setFirstSubfield() 函数原型如下:

        /// <summary>
        /// 设置指定名字的第一个字段和第一个子字段的值。如果没有这个字段,则创建一个;如果没有这个子字段,则创建一个
        /// </summary>
        /// <param name="strFieldName">字段名。3字符</param>
        /// <param name="strSubfieldName">子字段名。1字符</param>
        /// <param name="strContent">要设置的子字段内容</param>
        /// <param name="strNewIndicator">如果指定的字段不存在,则需要创建,创建的时候将采用本参数作为字段指示符的值</param>
        public void setFirstSubfield(
            string strFieldName,
            string strSubfieldName,
            string strContent,
            string strNewIndicator = "  ")
renyh commented 5 years ago

在MARC记录的998$s子字段作标识 (dp2circulation)

using System;
using System.Windows.Forms;

using dp2Circulation;
using DigitalPlatform.Marc;
using DigitalPlatform.Text;

public class add998s : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        string value = "***"; //这里输入标识值

        // 先检索出998字段
        MarcNodeList fields = this.MarcRecord.select("field[@name=998]");
        // 如果不存在998字段,直接增加998  $s
        if (fields.count == 0)
        {
            this.MarcRecord.append(String.Format("998  {0}s{1}", MarcQuery.SUBFLD, value));
            this.Changed = true;
            return;
        }

        // 取第一个998节点的$s子字段
        MarcNode node = fields[0];
        MarcNodeList subfields = node.select("subfield[@name='s']");
        // 如果没有$s子字段,增加998  $s
        if (subfields.count == 0)
        {
            node.append(String.Format("{0}s{1}", MarcQuery.SUBFLD, value));
            this.Changed = true;
            return;
        }
       // 取第一个998$s子字段
        MarcNode sNode = subfields[0];
        string sContent= sNode.Content;
        string old = sContent; //值
        StringUtil.SetInList(ref sContent, value, true); //新值

        //MessageBox.Show("old="+old +" new=" + sContent);
       // 如果新旧值不等,标记为修改状态
        if (old != sContent)
        {
            sNode.Content = sContent;
            this.Changed = true;
            return;
        }
    }
}

DigitalPlatform.Text.dll 的 SetInList()函数


        // 在列举值中增加或清除一个值
        // parameters:
        //      strSub  里面可以包含多个值
        public static void SetInList(ref string strList,
            string strSub,
            bool bOn)
        {
            if (bOn == false)
            {
                RemoveFromInList(strSub,
                    true,
                    ref strList);
            }
            else
            {
                // 单个值的情况
                if (strSub.IndexOf(',') == -1)
                {
                    if (IsInList(strSub, strList) == true)
                        return; // 已经有了

                    // 在尾部新增加
                    if (string.IsNullOrEmpty(strList) == false)
                        strList += ",";

                    strList += strSub;
                    return;
                }

                // 2012/2/2
                // 多个值的情况
                string[] sub_parts = strSub.Split(new char[] { ',' });
                foreach (string sub in sub_parts)
                {
                    if (sub == null)
                        continue;

                    string strOne = sub.Trim();
                    if (string.IsNullOrEmpty(strOne) == true)
                        continue;

                    if (IsInList(strOne, strList) == true)
                        continue;   // 已经有了

                    // 在尾部新增加
                    if (string.IsNullOrEmpty(strList) == false)
                        strList += ",";

                    strList += strOne;
                }
            }
        }

补充一下:上面的 MarcQuery 脚本可以作为一个很好的培训练习题材。示范在内务书目查询窗中,如何二次开发 MarcQuery 脚本。 但就“修改书目记录 998$s” 这个功能来说,内务的书目查询窗内是本来就有这个功能的。即浏览列表中上下文菜单中的“快速修改书目记录”功能。这个功能挺完备的,它可以为 998$s 添加或者去掉一个子串,而保留其他子串。 注:998$s 内容,也就是书目记录的“状态”字段,里面可以是这样的形态“子串1,子串2”。

原贴地址 https://github.com/DigitalPlatform/dp2/issues/229

DigitalPlatform commented 5 years ago

输出到 Excel 文件(用 ClosedXML 函数库实现)

创建一个名为 output_excel.cs 的文件:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform;
using DigitalPlatform.Core;
using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

using ClosedXML.Excel;

public class MyMarcQueryHost : MarcQueryHost
{
    SaveFileDialog dlg = new SaveFileDialog();
    XLWorkbook doc = null;
    IXLWorksheet sheet = null;
    int nRowIndex = 1;  // 行号

    public override void OnBegin(object sender, StatisEventArgs e)
    {
        // 询问文件名
        dlg.Title = "请指定要输出的 Excel 文件名";
        dlg.CreatePrompt = false;
        dlg.OverwritePrompt = true;
        dlg.Filter = "Excel 文件 (*.xlsx)|*.xlsx|All files (*.*)|*.*";

        dlg.RestoreDirectory = true;

        if (dlg.ShowDialog() != DialogResult.OK)
        {
            e.Continue = ContinueType.SkipAll;
            return;
        }

        try
        {
            doc = new XLWorkbook(XLEventTracking.Disabled);
            File.Delete(dlg.FileName);
        }
        catch (Exception ex)
        {
            e.ParamString = ExceptionUtil.GetAutoText(ex);
            e.Continue = ContinueType.Error;
            return;
        }

        sheet = doc.Worksheets.Add("表格");

        // 输出标题行
        {
            int nColIndex = 1;  // 列号

            sheet.Cell(nRowIndex, nColIndex++).SetValue("title");

            sheet.Cell(nRowIndex, nColIndex++).SetValue("author");

            sheet.Cell(nRowIndex, nColIndex++).SetValue("publisher");

            sheet.Cell(nRowIndex, nColIndex++).SetValue("publish time");

            nRowIndex++;
        }
    }

    public override void OnRecord(object sender, StatisEventArgs e)
    {
        int nColIndex = 1;  // 列号

        string title = this.MarcRecord.select("field[@name='200']/subfield[@name='a']").FirstContent;
        sheet.Cell(nRowIndex, nColIndex++).SetValue(title);

        string author = this.MarcRecord.select("field[@name='200']/subfield[@name='f']").FirstContent;
        sheet.Cell(nRowIndex, nColIndex++).SetValue(author);

        string publisher = this.MarcRecord.select("field[@name='210']/subfield[@name='c']").FirstContent;
        sheet.Cell(nRowIndex, nColIndex++).SetValue(publisher);

        string publish_time = this.MarcRecord.select("field[@name='210']/subfield[@name='d']").FirstContent;
        sheet.Cell(nRowIndex, nColIndex++).SetValue(publish_time);

        nRowIndex++;
    }

    public override void OnEnd(object sender, StatisEventArgs e)
    {
        doc.SaveAs(dlg.FileName);
        doc.Dispose();

        try
        {
            System.Diagnostics.Process.Start(dlg.FileName);
        }
        catch
        {

        }
    }
}

还要创建一个名为 output_excel.cs.ref 的文件: (和上面的文件一起,两个文件要处于同一个文件夹)

<?xml version="1.0" encoding="utf-8"?>
<root>
  <ref>system.data.dll</ref>
  <ref>%bindir%/ClosedXml.dll</ref>
</root>

ClosedXML 是一个开源的 Excel 处理函数库,在这里: https://github.com/ClosedXML/ClosedXML

它的 wiki: https://github.com/ClosedXML/ClosedXML/wiki

distinct2010 commented 5 years ago

主题词字段一些疑问:606内容$a心理学$x论文$x写作$x心理$j指南 甚至有更多个$x,我想修改某个$x的内容如何实现?

distinct2010 commented 5 years ago

00666nam0 2200229 450_ 001008859384 00520180217001401.0 010 ǂa978-7-5596-0929-8ǂb精装ǂdCNY68.00 100 ǂa20180217d2017 em y0chiy0110 ea 1010 ǂachi 102 ǂaCNǂb110000 105 ǂaa z 000yb 106 ǂar 2001 ǂa颠覆者ǂ9dian fu zheǂdǂe周鸿祎自传ǂf周鸿祎,范海涛著ǂgǂhǂi 210 ǂa北京ǂc北京联合出版公司ǂd2017 215 ǂa377页ǂdǂe 6060 ǂa互联网络ǂx企业家ǂx生平事迹ǂx互联网商业ǂx个人创业经历ǂy中国ǂz现代 690 ǂaK825.38=76ǂv5 701 0ǂa周鸿祎ǂ9zhou hong yiǂ4著 701 0ǂa范海涛ǂ9fan hai taoǂ4著 801 0ǂaCNǂb91MARCǂc20180217 998 ǂu2018-08-09 13:17:39Zǂzsupervisor


比如这条数据,我想要修改606的第三个$x内容 如果能够实现对606这个字段下面的所有子字段逐个进行修改时最好的,因为主题词字段多种多样,除了$a,$x还有$j$y$z等这样的

DigitalPlatform commented 5 years ago

@distinct2010

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        var fields = this.MarcRecord.select("field[@name='606']");
        foreach(MarcField field in fields)
        {
            var subfields =field.select("subfield[@name='x']");
            // 修改第三个 $x 子字段内容(注意代码里面下标是从 0 开始的)
            if (subfields.count > 2)
            {
                subfields[2].Content += "---";
                this.Changed = true;
            }
        }
    }
}
distinct2010 commented 5 years ago

前面的描述不太清楚,给定一个606字段,606字段的所有自字段内容是fields[0].ChildNodes.Contents[i] 现在有个数组,通过循环修改每个子字段内容

DigitalPlatform commented 5 years ago

@distinct2010

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        var fields = this.MarcRecord.select("field[@name='606']");
        foreach(MarcField field in fields)
        {
            int i = 0;
            foreach(MarcSubfield subfield in field.ChildNodes)
            {
                subfield.Content = (i++).ToString();
                this.Changed = true;
            } 
        }
    }
}
distinct2010 commented 5 years ago

//开始删除设置字段 MarcRecord records = new MarcRecord(this.strMarc); string[] DelFields = Settings.Default.DeleteField.Split(','); foreach (string DelField in DelFields) { if (DelField.Length == 3) { MarcNodeList fields = records.select("field[@name='" + DelField + "']"); foreach (MarcNode node in fields) { node.detach(); } } else if (DelField.Length == 4) { string zd = DelField.Substring(0, 3); string zzd = DelField.Substring(3, 1); MarcNodeList subfields = records.select("field[@name='" + zd + "']/subfield[@name='" + zzd + "']"); foreach (MarcNode node in subfields) { node.detach(); } } else { MessageBox.Show("删除字段配置项:" + DelField + " 有误!"); } }

报错:此时FocusedField不可能为null

DigitalPlatform commented 5 years ago

@distinct2010

上面您给出的代码不完整,没法验证运行。请提供完整的、能在 dp2circulation 或 dp2catalog 中运行的脚本文件内容。

DigitalPlatform commented 5 years ago

插入若干个同名字段

插入到同名字段的后方:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        for(int i =0;i<10;i++)
        {
            this.MarcRecord.ChildNodes.insertSequence(new MarcField('$', "801  $aCN"+i.ToString()+"$bNLC"), InsertSequenceStyle.PreferTail);
            this.Changed = true;
        }
    }
}

注意 insertSequence() 函数的第二参数是指定同名字段插入时候的相对位置关系。InsertSequenceStyle.PreferTail 表示插入在同名字段的后面。这个参数可以缺省,默认为 InsertSequenceStyle.PreferHead,也就是插入到同名字段的前面。

其实只要看看 MarcQuery 代码关于这个函数的原型就能发现这个参数。这个函数还有其他参数的版本。

DigitalPlatform commented 5 years ago

按照字段名(数字)大小对字段进行排序

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        this.MarcRecord.ChildNodes.sort((a, b)=>{
            return string.Compare(a.Name, b.Name);
        });
        this.Changed = true;
    }
}

sort() 函数接受一个回调函数。回调函数只需要比较 a 和 b 两个 MarcNode 对象的大小即可,返回一个整数。整数如何返回呢?可以简单理解为 a - b 的值。如果 a 小于 b,那么应该返回负数;相等应该返回 0,a 大于 b 则应该返回一个正数。

DigitalPlatform commented 5 years ago

按照字段指示符大小对字段进行排序

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        this.MarcRecord.ChildNodes.sort((a, b)=>{
            return string.Compare(a.Indicator, b.Indicator);
        });
        this.Changed = true;
    }
}

当然,这个例子没有啥实际意义,只是示范一下如何自由决定排序因素。

DigitalPlatform commented 5 years ago

把 909 字段里面的 $b 子字段复制到一个 905 字段中

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
                // 制造出若干 909 字段
        for(int i =0;i<10;i++)
        {
            this.MarcRecord.ChildNodes.insertSequence(new MarcField('$', "909  $aCN"+i.ToString()+"$bB" + i.ToString()), InsertSequenceStyle.PreferTail);
            this.Changed = true;
        }

                // 新添加一个 905 字段
        MarcField field = new MarcField('$', "905  $atest");
        this.MarcRecord.ChildNodes.insertSequence(field, InsertSequenceStyle.PreferTail);

                // 选择和复制 909$b 子字段,复制到刚才的 905 字段中
        MarcNodeList subfields = this.MarcRecord.select("field[@name='909']/subfield[@name='b']");
        foreach(MarcSubfield subfield in subfields)
        {
            field.ChildNodes.insertSequence(subfield.clone(), InsertSequenceStyle.PreferTail);
        }
    }
}
distinct2010 commented 5 years ago

删除所有9**字段如何实现?

DigitalPlatform commented 5 years ago

删除所有 9XX 字段

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        this.MarcRecord.select("field[substring(@name,1,1)='9']").detach();
        this.Changed = true;
    }
}

注意 XPath 的 substring() 函数第二参数 offset 是从 1 开始计数的,比较坑人。

DigitalPlatform commented 4 years ago

原来 BBS 里面 http://dp2003.com/dp2bbs/article.aspx?board=@__3&id=71 其中的代码修订以后放在这里:

第一个

using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Catalog;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
                this.MarcRecord.ChildNodes.add(new MarcField("200", "  ",SUBFLD + "aAAAA"));
                this.MarcRecord.ChildNodes.add(new MarcField("300", "  ", SUBFLD + "bBBBB"));

                MarcNodeList nodes = this.MarcRecord.select("field[@name='200']");

                MessageBox.Show(this.MainForm, nodes.count.ToString());

                this.Changed = true;
    }
}

第二个:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Catalog;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
                this.MarcRecord.ChildNodes.add(new MarcField("200", "  ",SUBFLD + "aAAAA"));
                this.MarcRecord.ChildNodes.add(new MarcField("300", "  ", SUBFLD + "bBBBB"));

                MarcNodeList nodes = this.MarcRecord.select("field[@name='200']");
                    nodes.first().Name = "999";    // 修改了第一个200字段的字段名
                this.Changed = true;
    }
}
distinct2010 commented 4 years ago

如何修改头标区和指示符呢?

DigitalPlatform commented 4 years ago

如何修改头标区

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        this.MarcRecord.Header[0, 5] = "77888";
        this.MarcRecord.Header[5, 1] = "!";
        this.Changed = true;
    }
}
GUI9527 commented 4 years ago

请高手写一个这样的脚本,把215▼a罗马数字转阿拉伯数字,例如,215▼axii,变成215▼a12,这是罗马小写的,另一种是罗马大写,例如215▼aVIII,也是转为215▼a8,非常感谢!

DigitalPlatform commented 4 years ago

把 215$a 中的罗马数字替换为阿拉伯数字

下面脚本要在 dp2catalog 中执行。罗马数字替换为阿拉伯数字的函数未实现全部功能,只示范性替换了 xiii 这个罗马数字。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Catalog;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        var subfields = this.MarcRecord.select("field[@name='215']/subfield[@name='a']");
        foreach(MarcSubfield subfield in subfields)
        {
            string content = subfield.Content;
            string new_content = ReplaceText(content);
            if (content != new_content)
            {
                subfield.Content = new_content;
                this.Changed = true;
            }
        }
    }

    // 把文本中的罗马数字部分替换为阿拉伯数字。未完成
    static string ReplaceText(string text)
    {
        if (string.IsNullOrEmpty(text))
            return text;
        // TODO: 这里需要实现变换功能
        return text.Replace("xiii", "13");
    }
}
GUI9527 commented 4 years ago

制作MARC21数据,常常需要转换一些字段内容,如020:$cCNY307.28(GBP29.99) [注:从v3编目marc记录窗复制▼到记事本变成了$,在复制$到记录窗没有变回▼],如何通过脚本把已经制作好的txt文件批加入,如文本文件制作好,9783944874593 :▼cCNY461.07(GBP45.00) [注:前面冒号必加] ,把▼c准确无误的加入对应数据020字段▼a9783944874593的后面

GUI9527 commented 4 years ago

符号转化对应上了 9780190904913 :|cCNY266.29(GBP25.99) 9780190906801 :|cCNY491.71(GBP47.99) 9780190908904 :|cCNY655.74(GBP64.00) 9780190909376 :|cCNY563.53(GBP55.00) 9780190913151 :|cCNY491.71(GBP47.99)脚本如何写还是个问题?????????????????????????? 01331cam a2200325 i 4500 00120415204 00520190523125802.0 008180321s2018 nyu b 001 0 eng__ 010 ǂa 2018013235 020 ǂa9780190904913 (hardcover)【注:把文本文件内容或excel的:|cCNY266.29(GBP25.99)加到这里来】 020 ǂz9780190904920 (updf) 020 ǂz9780190904937 (ebook) 040 ǂaDLCǂbengǂcDLCǂerdaǂdDLC 042 ǂapcc 05000ǂaBJ71ǂb.P46 2018 08200ǂa170.9ǂ223 093 ǂa 1001 ǂaPettit, Philip,ǂd1945-ǂeauthor. 24514ǂaThe birth of ethics :ǂbreconstructing the role and nature of morality /ǂcPhilip Pettit ; with commentary by Michael Tomasello ; edited by Kinch Hoekstra. 260 1ǂa[New York, NY] :ǂbOxford University Press,ǂc[2018] 300 ǂavi, 387 pages ;ǂc22 cm 336 ǂatextǂbtxtǂ2rdacontent 337 ǂaunmediatedǂbnǂ2rdamedia 338 ǂavolumeǂbncǂ2rdacarrier 504 ǂaIncludes bibliographical references (pages 359-369) and index. 650 0ǂaEthicsǂxHistory. 7001 ǂaHoekstra, Kinch,ǂeeditor. 906 ǂa7ǂbcbcǂcorignewǂd1ǂeecipǂf20ǂgy-gencatlg 9250 ǂaacquireǂb1 shelf copyǂxpolicy default 955 ǂbrk14 2018-03-21ǂcrk14 2018-03-21 telework to subjǂdrk02 2018-05-11 to Dewey (telework)ǂwxm07 2018-05-11ǂaxn16 2018-12-17 1 copy rec'd., to CIP ver.ǂark20 2018-12-22ǂfrf10 2019-01-17 CIP ver. to CALM 997 ǂa9780190904913|reconstructing the role and nature of morality,The birth of ethics|Hoekstra, Kinch,Pettit, Philip|Oxford University Press,2018ǂhe92a372245c74f5067db151eddd22204ǂv0.03


distinct2010 commented 4 years ago

删除所有的空字段和所有的空子字段如何实现?

Hopeshine commented 4 years ago

dp2内务的种册窗,在MARC编辑器,右键 “整理“-”删除全部空字段”/“删除全部子字段”等一组命令

distinct2010 commented 4 years ago

如何从record中得到指定字段名的MarcField对象? record.select("field[@name='字段名']").firstcontent;强制转换为MarcField好像不行; foreach (MarcField Field in record.Fields) { Field.Name=字段名; } 只能用这种方式吗?

distinct2010 commented 4 years ago

dp2内务的种册窗,在MARC编辑器中,单击“整理”-“删除全部空缺” /“删除全部子替换”等一组命令

MARC编辑器中,此命令是用MarcEdit中的命令完成的。 MarcQuery如何实现呢?

MarcNodeList Fields = record.ChildNodes;
            foreach (MarcNode Field in Fields)
            {
                MarcNodeList SubFields = Field.ChildNodes;
                foreach (MarcNode SubField in SubFields)
                {
                    if (string.IsNullOrEmpty(SubField.Content))
                    {
                        SubField.detach();
                    }
                }
                if (string.IsNullOrEmpty(Field.Content))
                {
                    Field.detach();
                }
            }
            return record;

这样是会报错的,因为foreach中集合已经被更新了

DigitalPlatform commented 3 years ago

@distinct2010

这样是会报错的,因为foreach中集合已经被更新了

可以这样写:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {

        var fields = new List<MarcNode>(this.MarcRecord.ChildNodes.List);   // 复制到一个新的集合
        foreach (var field in fields)
        {
            var subfields = new List<MarcNode>(field.ChildNodes.List);    // 复制到一个新的集合
            foreach (MarcNode subfield in subfields)
            {
                if (string.IsNullOrEmpty(subfield.Content))
                {
                    subfield.detach();
                    this.Changed = true;
                }
            }
            if (string.IsNullOrEmpty(field.Content))
            {
                field.detach();
                this.Changed = true;
            }
        }
    }
}

上面这个写法,是把 ChildNodes 复制到一个新的集合中(代码中的 fields 或者 subfields),对新的集合进行枚举遍历,就不怕原先的 ChildNodes 在中途被修改了,因为遍历的不是 ChildNodes。

DigitalPlatform commented 3 years ago

@distinct2010

如何从record中得到指定字段名的MarcField对象?

看一下这段示范代码:

var nodes = record.select("field[@name='字段名']");  // MarcNodeList 类型
var first_node = nodes[0];  // MarcNode 类型
var field = (MarcField)first_node;  // MarcField 类型

其中 select() 方法返回的是一个 MarcNode 类型的集合。注意,是集合,它不是一个元素,而是多个元素的集合。 把集合的第一个元素取出来,才是 MarcNode 类型的对象,也就是代码中的 first_node。 代码中最后 MarcNode 类型的对象可以强制转换为 MarcField 类型的对象,这是因为你知道 select() 选出来的是字段,这样转换是可行的。(MarcField 是 MarcNode 的派生类)

为啥 select() 返回的是 MarcNode 对象的集合,而不是 MarcField 对象的集合呢?因为 select() 选出来的对象,既可能是字段,也可能是子字段啊。所以不可能用 MarcField 类型,而是只能用 MarcField 和 MarcSubfield 共同的基类 MarcNode。MarcNode 这个类型就是“既可能是字段、也可能是子字段”这样一种类型。

record.select("field[@name='字段名']").firstcontent;强制转换为MarcField好像不行;

上面说了 select() 返回的是集合。也就是说选中的对象可能是好多个对象(不止一个),这是符合通用的需求的。但很多场合之下,二次开发者只想关心这个集合中的第一个元素,甚至第一个元素的文本内容,那么 MarcQuery 函数库就很贴心地为集合类(MarcNodeList)提供了 FirstContent 这个属性,用它可以直接获得第一个元素的 Content 属性值,等于这样就减少了书写代码的量。如果不减少,本来应该是这样:

var nodes = record.select("field[@name='字段名']");
var first_node = nodes[0];
var first_content = first_node.Content;

而且,这里示范的代码还没有处理 nodes 集合为空的情况,如果处理了还要增加一些代码量。所以 .FirstContent 是为了方便书写代码而提供的。

建议平时看一下 MarcQuery 的参考文档,弄清楚类、成员的类型: http://dp2003.com/marcquery/

直接看 MarcQuery 函数库的源代码也可以达到同样目的。

DigitalPlatform commented 3 years ago

如何删除空字段、空子字段

(MarcQuery (官方)代码示范)

最新为 MarcQuery 函数库中的 MarcRecord 类增加了几个删除空字段、空子字段的函数。可查看 GitHub 上的 MarcRecord.cs 源代码: https://github.com/DigitalPlatform/dp-library/blob/master/DigitalPlatform.MarcQuery/MarcRecord.cs

我把这几个函数的源代码抄录在这里:

        // 2020/11/14
        /// <summary>
        /// 删除全部空的字段、子字段
        /// </summary>
        /// <returns>返回已经被删除的对象(字段或者子字段)的集合</returns>
        public List<MarcNode> DetachEmptyFieldsSubfields()
        {
            var subfields = DetachEmptySubfields();
            var fields = DetachEmptyFields();
            var results = new List<MarcNode>();
            results.AddRange(fields.Cast<MarcNode>());
            results.AddRange(subfields.Cast<MarcNode>());
            return results;
        }

        // 2020/11/14
        /// <summary>
        /// 删除全部空的字段
        /// </summary>
        /// <returns>返回已经被删除的字段的集合</returns>
        public List<MarcField> DetachEmptyFields()
        {
            List<MarcField> results = new List<MarcField>();

            var fields = this.select("//field");
            foreach (MarcField field in fields)
            {
                if (string.IsNullOrEmpty(field.Content))
                {
                    field.detach();
                    results.Add(field);
                }
            }

            return results;
        }

        // 2020/11/14
        /// <summary>
        /// 删除全部空的子字段
        /// </summary>
        /// <returns>返回已经被删除的子字段的集合</returns>
        public List<MarcSubfield> DetachEmptySubfields()
        {
            List<MarcSubfield> results = new List<MarcSubfield>();

            var subfields = this.select("//subfield");
            foreach (MarcSubfield subfield in subfields)
            {
                if (string.IsNullOrEmpty(subfield.Content))
                {
                    subfield.detach();
                    results.Add(subfield);
                }
            }

            return results;
        }

删除空字段的函数,和删除空子字段的函数,用 select() 函数选出全部字段(或者子字段)对象,然后对内容为空的对象加以 detach() 即可。

注意选出全部字段或子字段对象的时候,XPath 式子里面用到了 // 表示遍历对象树的全部各级对象。如果不熟悉 XPath 用法可以在网上搜一些关于 XPath 的教程学习一下。

删除全部字段和子字段的函数,先删除全部空子字段,这可能会导致一些字段内容变成空,所以要先做这一步。然后再删除全部内容为空的字段。

还可以查看这几个函数的单元测试代码,帮助理解函数用途: https://github.com/DigitalPlatform/dp-library/blob/master/UnitTestMarcQuery/TestMarcRecord.cs

DigitalPlatform commented 3 years ago

给书目记录加拼音子字段

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;
using System.Drawing;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;
using DigitalPlatform.CommonControl;

public class MyMarcQueryHost : MarcQueryHost
{
    // 新的加拼音字段配置。$9
    string PinyinCfgXml = "<root>"
    + "<item name='200' from='a' to='9' />"
    + "<item name='701' indicator='@[^A].' from='a' to='9' />"
    + "<item name='711' from='a' to='9' />"
    + "<item name='702' indicator='@[^A].' from='a' to='9' />"
    + "<item name='712' from='a' to='9' />"
    + "</root>";

    public override void OnRecord(object sender, StatisEventArgs e)
    {

        GlobalParameters.ChangedBackColor = Color.DarkOrange;
        GlobalParameters.ChangedForeColor = Color.White;

        // RemovePinyin() 和 AddPinyin() 两个函数的原型可参看内务源代码:
                // https://github.com/DigitalPlatform/dp2/blob/master/dp2Circulation/MainForm/PinyinExtension.cs

        // 先删除书目记录中原有的拼音子字段
            this.MainForm.RemovePinyin(this.MarcRecord,
                    PinyinCfgXml,
                    "");

        // 然后加拼音
            int nRet = this.MainForm.AddPinyin(this.MarcRecord,
                    PinyinCfgXml,
                    PinyinStyle.None,
                    "",
                    false);
        this.Changed = true;

    }
}
DigitalPlatform commented 3 years ago

过滤出和“外国文学”有关的书目记录

有朋友希望寻找所有和外国文学有关的连环画的书目。通过主题词途径检索“连环画”可以检索出所有连环画的书目,但没法精确检索出其中的和外国文学有关的部分。606 字段 $y 里面虽然有国别名称,但一般来说 $y 并不建立检索点(只有 $a 建立了检索点),所以必须另想办法。

下面是一个示范的脚本代码,它可以从全部连环画的书目记录中进一步筛选出和外国文学有关的部分记录。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;
using System.Drawing;
using System.Web;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;
using DigitalPlatform.Text;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        List<string> names = new List<string>();
        bool found = false;
        {
            var subfields = this.MarcRecord.select("field[@name='606']/subfield[@name='y']");
            foreach(MarcSubfield subfield in subfields)
            {
                if (subfield.Content != "中国")
                {
                    names.Add(subfield.Text.ToString());
                }
            }
        }

        if (names.Count == 0)
        {
            var subfields = this.MarcRecord.select("field[@name='701' or @name='702' or @name='711' or @name='712']/subfield[@name='4' or @name='c']");
            foreach(MarcSubfield subfield in subfields)
            {
                if (subfield.Name == "4")
                {
                    if (subfield.Content == "译")
                    {
                        names.Add(subfield.Text.ToString());
                        break;
                    }
                }

                if (subfield.Name == "c")
                {
                    this.OutputText(subfield.Content);

                    if (subfield.Content.Contains("(英") 
                        || subfield.Content.Contains("(美)")
                        || subfield.Content.Contains("(德")
                        || subfield.Content.Contains("(东德")
                        || subfield.Content.Contains("(西德")
                        || subfield.Content.Contains("(古希腊")
                        || subfield.Content.Contains("(西")
                        || subfield.Content.Contains("(日")
                        || subfield.Content.Contains("(法")
                        || subfield.Content.Contains("(意")
                        || subfield.Content.Contains("(西")
                        || subfield.Content.Contains("(奥")
                        || subfield.Content.Contains("(波")
                        || subfield.Content.Contains("(丹")
                        || subfield.Content.Contains("(加")
                        || subfield.Content.Contains("(捷")
                        || subfield.Content.Contains("(瑞士")
                        || subfield.Content.Contains("(苏")
                        || subfield.Content.Contains("(前苏")
                        || subfield.Content.Contains("(印")
                        || subfield.Content.Contains("(阿")
                        || subfield.Content.Contains("(澳")
                        || subfield.Content.Contains("(挪")
                        || subfield.Content.Contains("(越")
                        || subfield.Content.Contains("(俄"))
                    {
                        names.Add(subfield.Text.ToString());
                        break;
                    }

                }
            }
        }

        if (names.Count > 0)
        {
            this.OutputText("匹配点:" + StringUtil.MakePathList(names, "; "));

            ((ListViewItem)this.UiItem).BackColor = Color.DarkRed;
            ((ListViewItem)this.UiItem).ForeColor = Color.White;

            ((ListViewItem)this.UiItem).Selected = true;
        }
        else
        {
            ((ListViewItem)this.UiItem).BackColor = Color.White;
            ((ListViewItem)this.UiItem).ForeColor = Color.Black;

            ((ListViewItem)this.UiItem).Selected = false;
        }
    }
}
windflower763 commented 3 years ago

修改字段名或子字段名**

using System; using System.Collections; using System.Collections.Generic; using System.Windows.Forms; using System.IO; using System.Text; using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc; using DigitalPlatform.Xml; using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost { public override void OnRecord(object sender, StatisEventArgs e) { MarcNodeList nodes = this.MarcRecord.select("field[@name='410']"); if (nodes.count > 0 ) { foreach (MarcNode node in nodes) node.Name = "461"; this.Changed = true; }
} }`

windflower763 commented 3 years ago

如何将200字段$f子字段内容分段填充到701字段,如将$f(美)肯·费雪(Ken Fisher), (美)劳拉·霍夫曼斯(Lara Hoffmans), (美)珍妮弗·周(Jennifer Chou)著填充为: $c(美)$a费雪$g(Ken Fisher)$4著 $c(美)$a霍夫曼斯$g(Hoffmans, Lara)$4著 $c(美)$a周$g(Chou, Jennifer)$4著

DigitalPlatform commented 3 years ago

@windflower763

如何将200字段$f子字段内容分段填充到701字段,如将$f(美)肯·费雪(Ken Fisher), (美)劳拉·霍夫曼斯(Lara Hoffmans), (美)珍妮弗·周(Jennifer Chou)著填充为: $c(美)$a费雪$g(Ken Fisher)$4著 $c(美)$a霍夫曼斯$g(Hoffmans, Lara)$4著 $c(美)$a周$g(Chou, Jennifer)$4著

准备工作

编写好一个用来切割著者字符串的函数,并编写相应的单元测试函数:

using System;
using System.Collections.Generic;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTestGeneral
{
    [TestClass]
    public class TestScript
    {
        [TestMethod]
        public void TestMethod_paseAuthorString_1()
        {
            var infos = ParseAuthorString("(美)肯·费雪(Ken Fisher)");
            Assert.AreEqual(1, infos.Count);

            Assert.AreEqual("(美)", infos[0].Country);
            Assert.AreEqual("肯·费雪", infos[0].Author);
            Assert.AreEqual("(Ken Fisher)", infos[0].OriginName);
            Assert.AreEqual(null, infos[0].CreateType);
        }

        [TestMethod]
        public void TestMethod_paseAuthorString_2()
        {
            var infos = ParseAuthorString("(美)肯·费雪(Ken Fisher), (美)劳拉·霍夫曼斯(Lara Hoffmans)");
            Assert.AreEqual(2, infos.Count);

            {
                var info = infos[0];
                Assert.AreEqual("(美)", info.Country);
                Assert.AreEqual("肯·费雪", info.Author);
                Assert.AreEqual("(Ken Fisher)", info.OriginName);
                Assert.AreEqual(null, info.CreateType);
            }

            {
                var info = infos[1];
                Assert.AreEqual("(美)", info.Country);
                Assert.AreEqual("劳拉·霍夫曼斯", info.Author);
                Assert.AreEqual("(Lara Hoffmans)", info.OriginName);
                Assert.AreEqual(null, info.CreateType);
            }
        }

        [TestMethod]
        public void TestMethod_paseAuthorString_3()
        {
            var infos = ParseAuthorString("(美)珍妮弗·周(Jennifer Chou)著");
            Assert.AreEqual(1, infos.Count);

            {
                var info = infos[0];
                Assert.AreEqual("(美)", info.Country);
                Assert.AreEqual("珍妮弗·周", info.Author);
                Assert.AreEqual("(Jennifer Chou)", info.OriginName);
                Assert.AreEqual("著", info.CreateType);
            }
        }

        public class AuthorInfo
        {
            public string Country { get; set; }
            public string Author { get; set; }
            public string OriginName { get; set; }
            public string CreateType { get; set; }

            // 获得下一个部分
            public static string NextPart(ref string text)
            {
                if (string.IsNullOrEmpty(text))
                    return null;

                string result = "";
                if (text[0] == '(')
                {
                    int pos = text.IndexOf(')');
                    if (pos == -1)
                    {
                        result = text;
                        text = "";
                        return result;
                    }

                    result = text.Substring(0, pos + 1);
                    text = text.Substring(pos + 1);
                    return result;
                }

                {
                    int pos = text.IndexOf('(');
                    if (pos == -1)
                    {
                        result = text;
                        text = "";
                        return result;
                    }

                    result = text.Substring(0, pos);
                    text = text.Substring(pos);
                    return result;
                }
            }

        }

        /*
         * 
如将$f(美)肯·费雪(Ken Fisher), (美)劳拉·霍夫曼斯(Lara Hoffmans), (美)珍妮弗·周(Jennifer Chou)著填充为:
$c(美)$a费雪$g(Ken Fisher)$4著
$c(美)$a霍夫曼斯$g(Hoffmans, Lara)$4著
$c(美)$a周$g(Chou, Jennifer)$4著
         * */
        static List<AuthorInfo> ParseAuthorString(string text)
        {
            List<AuthorInfo> results = new List<AuthorInfo>();
            // 先用逗号切割为多个区段
            var segments = text.Split(new char[] { ',' });
            foreach (var segment in segments)
            {
                string line = segment.Trim();

                var info = new AuthorInfo();
                while (true)
                {
                    var result = AuthorInfo.NextPart(ref line);
                    if (result == null)
                        break;
                    if (result[0] == '(')
                    {
                        if (string.IsNullOrEmpty(info.Country))
                            info.Country = result;
                        else
                            info.OriginName = result;
                        continue;
                    }

                    {
                        if (string.IsNullOrEmpty(info.Author))
                            info.Author = result;
                        else
                            info.CreateType = result;
                    }
                }

                results.Add(info);
            }

            return results;
        }
    }

}

这个部分恐怕要耗费一个熟练程序员大半天的工作时间。上面给出的也只是一个概念性的示范代码,并未经过严格的测试,一些特殊情况没有来得及考虑。

编写可用于 dp2circulation 的脚本程序

用到了上面编写好的切割字符串的函数。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {

        var text = this.MarcRecord.select("field[@name='200']/subfield[@name='f']").FirstContent;
        var infos = ParseAuthorString(text);
        foreach(var info in infos)
        {
            // 新添加一个 701 字段
            var field = new MarcField('$', "701  ");
            this.MarcRecord.ChildNodes.insertSequence(field, InsertSequenceStyle.PreferTail);
            if (string.IsNullOrEmpty(info.Country) == false)
                field.ChildNodes.add(new MarcSubfield("c", info.Country));
            if (string.IsNullOrEmpty(info.Author) == false)
                field.ChildNodes.add(new MarcSubfield("a", info.Author));
            if (string.IsNullOrEmpty(info.OriginName) == false)
                field.ChildNodes.add(new MarcSubfield("g", info.OriginName));
            if (string.IsNullOrEmpty(info.CreateType) == false)
                field.ChildNodes.add(new MarcSubfield("4", info.CreateType));
            this.Changed = true;
        }
    }

        public class AuthorInfo
        {
            public string Country { get; set; }
            public string Author { get; set; }
            public string OriginName { get; set; }
            public string CreateType { get; set; }

            // 获得下一个部分
            public static string NextPart(ref string text)
            {
                if (string.IsNullOrEmpty(text))
                    return null;

                string result = "";
                if (text[0] == '(')
                {
                    int pos = text.IndexOf(')');
                    if (pos == -1)
                    {
                        result = text;
                        text = "";
                        return result;
                    }

                    result = text.Substring(0, pos + 1);
                    text = text.Substring(pos + 1);
                    return result;
                }

                {
                    int pos = text.IndexOf('(');
                    if (pos == -1)
                    {
                        result = text;
                        text = "";
                        return result;
                    }

                    result = text.Substring(0, pos);
                    text = text.Substring(pos);
                    return result;
                }
            }

        }

        /*
         * 
如将$f(美)肯·费雪(Ken Fisher), (美)劳拉·霍夫曼斯(Lara Hoffmans), (美)珍妮弗·周(Jennifer Chou)著填充为:
$c(美)$a费雪$g(Ken Fisher)$4著
$c(美)$a霍夫曼斯$g(Hoffmans, Lara)$4著
$c(美)$a周$g(Chou, Jennifer)$4著
         * */
        static List<AuthorInfo> ParseAuthorString(string text)
        {
            List<AuthorInfo> results = new List<AuthorInfo>();
            // 先用逗号切割为多个区段
            var segments = text.Split(new char[] { ',' });
            foreach (var segment in segments)
            {
                string line = segment.Trim();

                var info = new AuthorInfo();
                while (true)
                {
                    var result = AuthorInfo.NextPart(ref line);
                    if (result == null)
                        break;
                    if (result[0] == '(')
                    {
                        if (string.IsNullOrEmpty(info.Country))
                            info.Country = result;
                        else
                            info.OriginName = result;
                        continue;
                    }

                    {
                        if (string.IsNullOrEmpty(info.Author))
                            info.Author = result;
                        else
                            info.CreateType = result;
                    }
                }

                results.Add(info);
            }

            return results;
        }

}

(701 字段的字段指示符没有来得及考虑。另外是否都创建为 701 也是一个问题。凡是涉及到这种具体细节的程序,面对的事务一定是非常繁杂的,上面是概念示范代码,若要变成真正可用的程序还需要额外做出很多努力才行)

GUI9527 commented 3 years ago

按照细分指示符大小对细分进行排序

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
  public override void OnRecord(object sender, StatisEventArgs e)
  {
      this.MarcRecord.ChildNodes.sort((a, b)=>{
          return string.Compare(a.Indicator, b.Indicator);
      });
      this.Changed = true;
  }
}

当然,这个例子没有啥实际意义,只是示范一下如何自由决定排序因素。

如果要写成按指示符大小删除字段,该怎么写呢?例如,要删除Indicator1=1,Indicator2=7的字段,字段名name=650,这是指定字段名,另一种情况是不指定字段名的,直接按指示符删,只要有这个指示符的所有字段一起删除。

DigitalPlatform commented 3 years ago

为书目记录添加 905$d$e

检索书目记录下属的册记录,取第一个非空的索取号,然后拆分为 $d 和 $e 添加到书目记录的 905 字段中。

代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;
using System.Drawing;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;
using DigitalPlatform.Text;
using DigitalPlatform.CommonControl;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        GlobalParameters.ChangedBackColor = Color.DarkOrange;
        GlobalParameters.ChangedForeColor = Color.White;

        var form = this.UiForm as MyForm;
        var channel = form.GetChannel();
        try
        {
            string strResult;
            string strError;
                var ret = Utility.GetSubRecords(
                channel,
                null,
                this.RecordPath,
                "firstAccessNo",
                    out strResult,
                    out strError);
            if (ret == -1)
            {
                e.Continue = ContinueType.Error;
                e.ParamString = strError;
                return;
            }

            var parts = StringUtil.ParseTwoPart(strResult, "/");
            string text_d = parts[0];
            string text_e = parts[1];
                    this.MarcRecord.setFirstSubfield("905", "d", text_d);
                    this.MarcRecord.setFirstSubfield("905", "e", text_e);

            this.Changed = true;
        }
        finally
        {
            form.ReturnChannel(channel);
        }
    }
}
windflower763 commented 2 years ago

请问如何对某一字段的子字段按指定顺序排序?比如701字段按$c$a$A$g$f$4的顺序?谢谢

DigitalPlatform commented 2 years ago

导出书目记录到 Excel 文件的脚本

在内务书目查询窗执行。

output_excel.cs

// 2021/10/9

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform;
using DigitalPlatform.Core;
using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;
using DigitalPlatform.Text;

using ClosedXML.Excel;

public class MyMarcQueryHost : MarcQueryHost
{
    SaveFileDialog dlg = new SaveFileDialog();
    XLWorkbook doc = null;
    IXLWorksheet sheet = null;
    int nRowIndex = 1;  // 行号

    static string[] columns = new string[] {
    "ID",
    "题名(200#a)",
    "并列题名(200#d)",
    "副题名(200#e)",
    "分辑号(200#h)",
    "版本项(205#a)",
    "丛编题名(225#a)",
    "并列丛编题名(225#d)",
    "附属丛编名(225#i)",
    "ISBN(010#a)",
    "出版者(210#c)",
    "出版日期(210#d)",
    "价格(010#d)  ",
    "作者(200#f)  ",
    "译者(200#g)  ",
    "作品语种(101#a)",
    "出版国别(102#a)",
    "中图法类号版本(690#v)",
    "页数(215$a)",
    "开本(215$d)",
    "摘要(330#a)",
    "分类编码(690#a)",
    };

    public override void OnBegin(object sender, StatisEventArgs e)
    {
        // 询问文件名
        dlg.Title = "请指定要输出的 Excel 文件名";
        dlg.CreatePrompt = false;
        dlg.OverwritePrompt = true;
        dlg.Filter = "Excel 文件 (*.xlsx)|*.xlsx|All files (*.*)|*.*";

        dlg.RestoreDirectory = true;

        if (dlg.ShowDialog() != DialogResult.OK)
        {
            e.Continue = ContinueType.SkipAll;
            return;
        }

        try
        {
            doc = new XLWorkbook(XLEventTracking.Disabled);
            File.Delete(dlg.FileName);
        }
        catch (Exception ex)
        {
            e.ParamString = ExceptionUtil.GetAutoText(ex);
            e.Continue = ContinueType.Error;
            return;
        }

        sheet = doc.Worksheets.Add("表格");

        // 输出标题行
        {
            int nColIndex = 1;  // 列号

            foreach (string title in columns)
            {
                sheet.Cell(nRowIndex, nColIndex++).SetValue(title);
            }
            nRowIndex++;
        }
    }

    public override void OnRecord(object sender, StatisEventArgs e)
    {
        int nColIndex = 1;  // 列号

        foreach (string name in columns)
        {
            if (name == "ID")
            {
                sheet.Cell(nRowIndex, nColIndex++).SetValue(this.RecordPath);
                continue;
            }

            var parts = StringUtil.ParseTwoPart(name, "(");
            if (string.IsNullOrEmpty(parts[1]))
            {
                nColIndex++;
                continue;
            }

            string field_name = parts[1].Substring(0, 3);
            string subfield_name = parts[1].Substring(4, 1);

            string value = this.MarcRecord.select("field[@name='"+field_name+"']/subfield[@name='"+subfield_name+"']").FirstContent;
            sheet.Cell(nRowIndex, nColIndex++).SetValue(value);
        }

        nRowIndex++;
    }

    public override void OnEnd(object sender, StatisEventArgs e)
    {
        doc.SaveAs(dlg.FileName);
        doc.Dispose();

        try
        {
            System.Diagnostics.Process.Start(dlg.FileName);
        }
        catch
        {

        }
    }
}

output_excel.cs.ref

<?xml version="1.0" encoding="utf-8"?>
<root>
  <ref>system.data.dll</ref>
  <ref>%bindir%/ClosedXml.dll</ref>
</root>

两个文件要放在同一个子目录。

DigitalPlatform commented 2 years ago

如何修改字段指示符

MarcField 对象有三个成员可用: Indicator (两个指示符一起,string 类型) Indicator1 (指示符1, char 类型) Indicator2 (指示符2, char 类型) 示范代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {

        var fields = this.MarcRecord.select("field[@name='200']");
        foreach(MarcField field in fields)
        {
            field.Indicator1 = '5';
            field.Indicator2 = '6';
            // field.Indicator = "78";
            this.Changed = true;
        }
    }
}
zhangyc310 commented 2 years ago

写得很好,可以做得更好。

这些字段名可以放到另外的配置文件中,以后就不用修改程序、重新编译了,要求导出字段不同时,修改一下配置文件即可。

DigitalPlatform commented 2 years ago

请问如何对某一字段的子字段按指定顺序排序?比如701字段按$c$a$A$g$f$4的顺序?谢谢

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Text;
using System.Xml;

using dp2Circulation;

using DigitalPlatform.Marc;
using DigitalPlatform.Xml;
using DigitalPlatform.Script;

public class MyMarcQueryHost : MarcQueryHost
{
    public override void OnRecord(object sender, StatisEventArgs e)
    {
        var fields = this.MarcRecord.select("field[@name='701']");
        foreach(MarcField field in fields)
        {
            field.ChildNodes.sort((a, b)=>{
                int index1 = GetNameIndex(a.Name[0]);
                int index2 = GetNameIndex(b.Name[0]);
                return index1 - index2;
                // return string.Compare(a.Name, b.Name);
                });
            this.Changed = true;
        }
    }

    static int GetNameIndex(char name)
    {
        char [] seq = new char [] {'c','a','A','g','f','4'};
        int index = Array.IndexOf(seq, name);
        return index;
    }
}