参考:http://www.cvanalyze.com/demo
可以将不同格式的(word,pdf,txt,html等)简历解析成统一的格式,从简历文本中提取出候选人的结构化字段,包括姓名,性别,年龄,联系方式,毕业院校,工作单位,求职意向...等等。并将简历导入数据库
1.工作经验等关键词,可能不同简历中有不同的说法,内容有可能是文本,也可能是表格。
2.如何对关键词进行提取?算法之类的怎么弄?
该怎么做?
麻烦大神给点思路。
------解决思路----------------------
真没用过
/// <summary>
/// 根据模板和一个DataTable得到一个解析字符串
/// </summary>
/// <param name="template">字符串模板</param>
/// <param name="dt">一个DataTable</param>
/// <returns>返回一个解析模板后的字符串</returns>
public static string Render(string template, DataTable dt)
{
if (dt == null
------解决思路----------------------
dt.Rows.Count < 1)
{
return "";
}
StringBuilder sb = new StringBuilder(1000);
List<string> arr;
List<string> columns;
Analyze(template, out arr, out columns);
int i;
foreach (DataRow row in dt.Rows)
{
i = 0;
for (; i < columns.Count; i++)
{
sb.Append(arr[i]);
sb.Append(row[columns[i]].ToString());
}
if (i < arr.Count)
{
sb.Append(arr[i]);
}
}
return sb.ToString();
}
/// <summary>
/// 根据模板和一个DataTable得到一个解析字符串
/// </summary>
/// <param name="template">字符串模板</param>
/// <param name="dt">一个DataTable</param>
/// <returns>返回一个解析模板后的字符串</returns>
/// <param name="rowCount">定义个行数,每隔此行数时调用传入的委托</param>
/// <param name="act">一个处理输出字符串的委托</param>
/// <returns></returns>
public static string Render(string template, DataTable dt,int rowCount,Action<StringBuilder> act)
{
if (dt == null
------解决思路----------------------
dt.Rows.Count < 1)
{
return "";
}
StringBuilder sb = new StringBuilder(1000);
List<string> arr;
List<string> columns;
Analyze(template, out arr, out columns);
int i;
//计算出每隔多少行调用一次act
rowCount = rowCount > 0 ? rowCount : dt.Rows.Count + 1;
int rowNum = 0;
foreach (DataRow row in dt.Rows)
{
rowNum++;
i = 0;
for (; i < columns.Count; i++)
{
sb.Append(arr[i]);
sb.Append(row[columns[i]].ToString());
}
if (i < arr.Count)
{
sb.Append(arr[i]);
}
if (rowNum % rowCount == 0)
{
act(sb);
}
}
return sb.ToString();
}
/// <summary>
/// 根据模板和一个泛型实体集合得到一个解析字符串
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <param name="template">字符串模板</param>
/// <param name="list">实体集合</param>
/// <returns>返回一个解析模板后的字符串</returns>
public static string Render<T>(string template, List<T> list)
{
if (list == null
------解决思路----------------------
list.Count < 1)
{
return "";
}
StringBuilder sb = new StringBuilder(1000);
List<string> arr;
List<string> columns;
Analyze(template, out arr, out columns);
int i;
Type type = typeof(T);
foreach (T item in list)
{
i = 0;
for (; i < columns.Count; i++)
{
sb.Append(arr[i]);
sb.Append(GetValue(type, item, columns[i]));
}
if (i < arr.Count)
{
sb.Append(arr[i]);
}
}
return sb.ToString();
}
/// <summary>
/// 对一个字符串模板进行分析
/// </summary>
/// <param name="template">字符串模板</param>
/// <param name="arr">存储除开列名的其他文本的集合</param>
/// <param name="columns">存储列名的集合</param>
static void Analyze(string template, out List<string> arr, out List<string> columns)
{
arr = new List<string>();
columns = new List<string>();
int previousEndIndex = 0;
//找到{xxx}
int startIndex = template.IndexOf('{');
int endIndex = template.IndexOf('}');
while (startIndex != -1)
{
//存储上一个}和现在搜索到的{之间的字符串,如果是第一次搜索到{,那么previousEndIndex=0则存储的是字符串起始到第一个{之间的字符串
arr.Add(template.Substring(previousEndIndex, startIndex - previousEndIndex));
//存储列名
columns.Add(template.Substring(startIndex + 1, endIndex - startIndex - 1));
startIndex++;
endIndex++;
previousEndIndex = endIndex;
startIndex = template.IndexOf('{', startIndex);
endIndex = template.IndexOf('}', endIndex);
}
//如果模板不是以}结尾,说明后面还有字符串
if (previousEndIndex < template.Length)
{
arr.Add(template.Substring(previousEndIndex));
}
//方法执行到此处,arr.Length==columns.Length或者arr.Length==columns.Length+1
}
/// <summary>
/// 根据一个实体类型,实体实例和属性名,获取属性值
/// </summary>
/// <param name="type">实体类型</param>
/// <param name="item">实体实例</param>
/// <param name="attrName">属性吗</param>
/// <returns></returns>
static string GetValue(Type type, object item, string attrName)
{
PropertyInfo property = type.GetProperty(attrName, BindingFlags.Public
------解决思路----------------------
BindingFlags.IgnoreCase
------解决思路----------------------
BindingFlags.Instance);
object obj = property.GetValue(item, null);
if (obj != null)
{
return obj.ToString();
}
return "";
}
/// <summary>
/// 根据模板,使用正则匹配然后替换输出字符串
/// </summary>
/// <param name="template"></param>
/// <param name="dt"></param>
/// <returns></returns>
public static string RenderTemplate(string template, DataTable dt)
{
StringBuilder sb = new StringBuilder(1000);
MatchCollection matches = reg.Matches(template);
string rowStr = template;
foreach (DataRow row in dt.Rows)
{
rowStr = template;
foreach (Match match in matches)
{
rowStr = rowStr.Replace(match.Value, row[match.Groups[1].Value].ToString());
}
sb.Append(rowStr);
sb.Append("\n");
}
return sb.ToString();
}
------解决思路----------------------
做过简历管理软件,支持纯文本和html解析,不过简历解析的思路与算法不过不能说,测试了一下你提供的那个解析器,解析准确率比我的解析器差多了
------解决思路----------------------
简历解析器就是文本解析器,对关键词及其内容进行分拣,由于目标格式变化多样,首先要创建一个通用解析器,通用解析器的解析准确度是衡量解析能力最重要的指标,对于某些不规范或完全不规范的内容,应建立特定解析器进行辅助解析。
这个演示网站的通用解析器完全不行,他们提供的简历范本的解析都是特定解析,也就是根据简历的格式(如:51job、智联招聘等简历格式)写死规则的,一旦格式变化,就失效了。
------解决思路----------------------
将Excel数据表读取到文本文件
private void btn_Select_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Excel文件
------解决思路----------------------
*.xls";//设置打开文件筛选器
openFileDialog1.Title = "选择Excel文件";//设置打开对话框标题
openFileDialog1.Multiselect = false;//设置打开对话框中只能单选
if (openFileDialog1.ShowDialog() == DialogResult.OK)//判断是否选择了文件
{
txt_Path.Text = openFileDialog1.FileName;//在文本框中显示Excel文件名
CBoxBind();//对下拉列表进行数据绑定
}
}
private void btn_Txt_Click(object sender, EventArgs e)
{
//连接Excel数据库
OleDbConnection olecon = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + txt_Path.Text + ";Extended Properties=Excel 8.0");
olecon.Open();//打开数据库连接
OleDbDataAdapter oledbda = new OleDbDataAdapter("select * from [" + cbox_SheetName.Text + "$]", olecon);//从工作表中查询数据
DataSet myds = new DataSet();//实例化数据集对象
oledbda.Fill(myds);//填充数据集
StreamWriter SWriter = new StreamWriter(cbox_SheetName.Text + ".txt", false, Encoding.Default);//实例化写入流对象
string P_str_Content = "";//存储读取的内容
for (int i = 0; i < myds.Tables[0].Rows.Count; i++)//遍历数据集中表的行数
{
for (int j = 0; j < myds.Tables[0].Columns.Count; j++)//遍历数据集中表的列数
{
P_str_Content += myds.Tables[0].Rows[i][j].ToString() + " ";//记录当前遍历到的内容
}
P_str_Content += Environment.NewLine;//字符串换行
}
SWriter.Write(P_str_Content);//先文本文件中写入内容
SWriter.Close();//关闭写入流对象
SWriter.Dispose();//释放写入流所占用的资源
MessageBox.Show("已经将" + cbox_SheetName.Text + "工作表中的数据成功写入到了文本文件中", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void CBoxBind()//对下拉列表进行数据绑定
{
cbox_SheetName.Items.Clear();//清空下拉列表项
//连接Excel数据库
OleDbConnection olecon = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + txt_Path.Text + ";Extended Properties=Excel 8.0");
olecon.Open();//打开数据库连接
System.Data.DataTable DTable = olecon.GetSchema("Tables");//实例化表对象
DataTableReader DTReader = new DataTableReader(DTable);//实例化表读取对象
while (DTReader.Read())//循环读取
{
string P_str_Name = DTReader["Table_Name"].ToString().Replace('$', ' ').Trim();//记录工作表名称
if (!cbox_SheetName.Items.Contains(P_str_Name))//判断下拉列表中是否已经存在该工作表名称
cbox_SheetName.Items.Add(P_str_Name);//将工作表名添加到下拉列表中
}
DTable = null;//清空表对象
DTReader = null;//清空表读取对象
olecon.Close();//关闭数据库连接
cbox_SheetName.SelectedIndex = 0;//设置下拉列表默认选项为第一项
}
------解决思路----------------------
有些人写的简历,人看着都费劲,别说让机器读了
真能读出来,你程序比人智商高.
------解决思路----------------------
模板,规范,规定死了会简单很多