├── .gitignore ├── README.md ├── course_table.xlsx ├── go.mod ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | run.exe 2 | main.exe 3 | output.ics -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SHU-ICSGenerator 2 | 完工啦,快乐 3 | 4 | 目前稳定(?)版本:[SHU-ICSGenerator v1.0](https://github.com/LuminolT/SHU-ICSGenerator/releases/tag/v1.0) 5 | 6 | ## 1 使用方法 7 | 8 | 1. 在教务处官网的课表查询处([网址](http://xk.autoisp.shu.edu.cn/StudentQuery/QueryCourseTable))复制表格,粘贴入excel样表(请注意行列对应); 9 | 2. 运行`run.exe`; 10 | 3. 将`output.ics`发送到手机; 11 | 4. iOS:从Safari打开导入; 12 | Android:从日历打开导入; 13 | 6. 是不是很简单,来,试试看。 14 | 15 | ## 2 TODO 16 | 17 | 1. √适配节假日(主要是冬季学期会被切开) 18 | 2. 适配表格(放宽格式要求) 19 | 20 | ## 3 Q&A 21 | 22 | 1. 为什么不直接从教务处爬数据呢? 23 | 24 | 违反了《数据安全法》《网络安全法》相关规定,网安学子要以网络安全为重。 25 | 26 | 2. 为什么我的课识别不了? 27 | 28 | 由于学校排课时间的描述多且杂,目前仅适配了计算机学院的课程内容,但欢迎PR! 29 | 30 | 31 | -------------------------------------------------------------------------------- /course_table.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuminolT/SHU-ICSGenerator/7ffea526d6ff10f42b745ce74683eb7d6a2e9b27/course_table.xlsx -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/arran4/golang-ical v0.0.0-20210825232153-efac1f4cb8ac // indirect 7 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect 8 | github.com/richardlehane/mscfb v1.0.3 // indirect 9 | github.com/richardlehane/msoleps v1.0.1 // indirect 10 | github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 // indirect 11 | github.com/xuri/excelize/v2 v2.4.1 // indirect 12 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect 13 | golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect 14 | golang.org/x/text v0.3.6 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/arran4/golang-ical v0.0.0-20210825232153-efac1f4cb8ac h1:O/TDwS98y/3RCIpZKpZyFEnzV1Uc46ZXgrMxpL33b+s= 2 | github.com/arran4/golang-ical v0.0.0-20210825232153-efac1f4cb8ac/go.mod h1:BSTTrYHuM12oAL8jDdcmPdw02SBThKYWNFHQlvEG6b0= 3 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 7 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 8 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 9 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= 10 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= 11 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI= 14 | github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= 15 | github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o= 16 | github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 19 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 20 | github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 h1:EpI0bqf/eX9SdZDwlMmahKM+CDBgNbsXMhsN28XrM8o= 21 | github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= 22 | github.com/xuri/excelize/v2 v2.4.1 h1:veeeFLAJwsNEBPBlDepzPIYS1eLyBVcXNZUW79exZ1E= 23 | github.com/xuri/excelize/v2 v2.4.1/go.mod h1:rSu0C3papjzxQA3sdK8cU544TebhrPUoTOaGPIh0Q1A= 24 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= 25 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 26 | golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 27 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 28 | golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8= 29 | golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 30 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 31 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 32 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 33 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 34 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 35 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 36 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 37 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 38 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 39 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 40 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 41 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 42 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 43 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "io" 7 | "os" 8 | "regexp" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | ics "github.com/arran4/golang-ical" 14 | "github.com/xuri/excelize/v2" 15 | ) 16 | 17 | type courseTime struct { 18 | start [2]int 19 | end [2]int 20 | day int // 1 for Monday etc 21 | week [11]int // 12345678910 week[0]: 1-新研, 2-行政, 3-单周, 4-双周, 5-正常 22 | startWeek int 23 | } 24 | 25 | type course struct { 26 | name string 27 | room string 28 | courseTime 29 | } 30 | 31 | // func courseSet(nameInfo, timeInfo, roomInfo string) ([]course, error) { 32 | // var temp course 33 | // courseList := make([]course, 0) 34 | // temp.name = nameInfo 35 | // temp.room = roomInfo 36 | 37 | // return courseList 38 | // } 39 | 40 | func main() { 41 | // Initiate Time 42 | var SYEAR, SDAY int 43 | var SMONTH time.Month 44 | var GapWeek int 45 | var EndWeek int 46 | fmt.Println("请输入本学期第一周周一的年月日(如2021-9-6):") 47 | fmt.Scanf("%d-%d-%d\n", &SYEAR, &SMONTH, &SDAY) 48 | if SMONTH > time.September { 49 | fmt.Println("检测到本学期为冬季学期,请输入寒假前最后一周的周数和寒假时长(如8-4表示第八周结束开始放假,放4周)") 50 | fmt.Scanf("%d-%d\n", &EndWeek, &GapWeek) 51 | } 52 | // SYEAR = 2021 53 | // SMONTH = 9 54 | // SDAY = 6 55 | // Read Table 56 | courseList, err := readTable("course_table.xlsx", "Sheet1") 57 | if err != nil { 58 | fmt.Println(err) 59 | } 60 | TIME_LOCATION, err := time.LoadLocation("Asia/Shanghai") 61 | if err != nil { 62 | TIME_LOCATION = time.FixedZone("CST", 8*3600) 63 | fmt.Println(err) 64 | } 65 | // fmt.Println(courseList) 66 | cal := ics.NewCalendar() 67 | cal.SetMethod(ics.MethodRequest) 68 | for _, coursePiece := range courseList { 69 | // fmt.Println(coursePiece) // Test 70 | 71 | // 🤬🤬🤬🤬🤬 72 | tempStartTime := time.Date(SYEAR, SMONTH, SDAY, coursePiece.start[0], coursePiece.start[1], 0, 0, TIME_LOCATION) 73 | tempEndTime := time.Date(SYEAR, SMONTH, SDAY, coursePiece.end[0], coursePiece.end[1], 0, 0, TIME_LOCATION) 74 | // fmt.Println(coursePiece.day) 75 | tempStartTime = tempStartTime.AddDate(0, 0, coursePiece.day-1) 76 | // tempStartTime = tempStartTime.AddDate(0, 0, 7*(coursePiece.startWeek-1)) 77 | tempEndTime = tempEndTime.AddDate(0, 0, coursePiece.day-1) 78 | // tempEndTime = tempEndTime.AddDate(0, 0, 7*(coursePiece.startWeek-1)) 79 | // 由于要加入冬季学期寒假的判断,下面就不用重复的了。 80 | /* 81 | switch coursePiece.week[0] { 82 | case 1: //新生研讨课 83 | event.AddRrule(fmt.Sprintf("FREQ=WEEKLY;INTERVAL=%d;COUNT=%d", 1, 5)) 84 | case 2: //形势政策课 85 | event.AddRrule(fmt.Sprintf("FREQ=WEEKLY;INTERVAL=%d;COUNT=%d", 5, 2)) 86 | case 3: //单周 87 | event.AddRrule(fmt.Sprintf("FREQ=WEEKLY;INTERVAL=%d;COUNT=%d", 2, 5)) 88 | case 4: //双周 89 | event.AddRrule(fmt.Sprintf("FREQ=WEEKLY;INTERVAL=%d;COUNT=%d", 2, 5)) 90 | case 5: //正常 91 | event.AddRrule(fmt.Sprintf("FREQ=WEEKLY;INTERVAL=%d;COUNT=%d", 1, 10)) 92 | } 93 | */ 94 | // fmt.Println(tempStartTime, "\n", tempEndTime) 95 | // fmt.Println(coursePiece.name, coursePiece.week) 96 | for i := 1; i <= 10; i++ { 97 | if coursePiece.week[i] == 1 { 98 | //Hash ID Check 99 | h := sha256.New() 100 | plaintext := fmt.Sprintf("%s%d%d", coursePiece.name, coursePiece.courseTime.day, coursePiece.courseTime.start) 101 | // fmt.Println(plaintext) 102 | h.Write([]byte(plaintext)) 103 | id := fmt.Sprintf("%x@%s", h.Sum(nil), "ical") // get HashValue in SHA256, used as EVENTID 104 | // new a pointer of cal.EEvent 105 | event := cal.AddEvent(id) 106 | // Check Real Week 107 | finalStartTime := tempStartTime.AddDate(0, 0, 7*(i-1)) 108 | finalEndTime := tempEndTime.AddDate(0, 0, 7*(i-1)) 109 | if i > EndWeek { 110 | finalStartTime = finalStartTime.AddDate(0, 0, 7*GapWeek) 111 | finalEndTime = finalEndTime.AddDate(0, 0, 7*GapWeek) 112 | } 113 | event.SetStartAt(finalStartTime) 114 | event.SetEndAt(finalEndTime) 115 | event.SetSummary(coursePiece.name) 116 | event.SetLocation(coursePiece.room) 117 | alarm := event.AddAlarm() 118 | // advancedTime := fmt.Sprintf("-PT%dM", ADVANCEDTIME) 119 | alarm.SetTrigger("-PT10M") 120 | } 121 | } 122 | // alarm.SetAction() 123 | } 124 | // fmt.Println(cal.Serialize()) 125 | err2 := WriteFile("./output.ics", []byte(cal.Serialize()), 0666) 126 | if err2 != nil { 127 | fmt.Println(err) 128 | } else { 129 | fmt.Println("成功写入") 130 | } 131 | fmt.Println("按任意键退出") 132 | b := make([]byte, 1) 133 | os.Stdin.Read(b) 134 | os.Stdin.Read(b) 135 | } 136 | 137 | func readTable(fileName, sheetName string) ([]course, error) { 138 | //ReadCourse 139 | courseList := make([]course, 0) 140 | xlFile, err := excelize.OpenFile(fileName) 141 | if err != nil { 142 | return courseList, err 143 | } 144 | rows, err := xlFile.GetRows(sheetName) 145 | if err != nil { 146 | return courseList, err 147 | } 148 | for rowIdx, row := range rows { 149 | tempRunes := []rune(row[0]) //Row Front Slice 150 | if rowIdx == 0 { 151 | continue 152 | } 153 | if tempRunes[0] > 'Z' { 154 | break 155 | } 156 | //每次读到时间,要先进行切片处理 157 | // tempCourseList, err := courseSet(row[2], row[6], row[7]) 158 | // if err != nil { 159 | // return courseList, err 160 | // } 161 | // row[6] for timeInfo 162 | var tempCourse course 163 | tempCourse.name = row[2] 164 | tempCourse.room = row[7] 165 | tempTimeList, _ := timeHandle(row[6]) 166 | for _, tempTime := range tempTimeList { 167 | tempCourse.courseTime = tempTime 168 | courseList = append(courseList, tempCourse) 169 | } 170 | } 171 | return courseList, err 172 | } 173 | 174 | func timeHandle(timeInfo string) ([]courseTime, error) { 175 | // fmt.Println(timeInfo) 176 | var tempTime courseTime 177 | timeList := make([]courseTime, 0) 178 | //第一遍,筛里面有没有周这个字,两种情况,1-5周,6-10周这种,1,6周,2,7周这种 179 | //第二遍,做切分,切分的时候根据里面有没有单双周进行一个判断 180 | splitFunc := func(r rune) bool { return r == ' ' || r == '(' || r == ')' } 181 | timeInfoSlice := strings.FieldsFunc(timeInfo, splitFunc) 182 | //Check 183 | //新生研讨课 184 | judgeXy, err := regexp.MatchString("[0-9]-[0-9]周", timeInfoSlice[len(timeInfoSlice)-1]) 185 | if err != nil { 186 | return timeList, err 187 | } 188 | if judgeXy { 189 | if timeInfoSlice[len(timeInfoSlice)-1][0] == '1' { 190 | tempTime.startWeek = 1 191 | for i := 1; i <= 5; i++ { 192 | tempTime.week[i] = 1 193 | } 194 | } else { 195 | tempTime.startWeek = 6 196 | for i := 6; i <= 10; i++ { 197 | tempTime.week[i] = 1 198 | } 199 | } 200 | tempTime.week[0] = 1 201 | } 202 | //形式政策课 203 | judgeXszc, err := regexp.MatchString("[0-9]周,[0-9]周", timeInfoSlice[len(timeInfoSlice)-1]) 204 | if err != nil { 205 | return timeList, err 206 | } 207 | if judgeXszc { 208 | temp, err := strconv.Atoi(timeInfoSlice[len(timeInfoSlice)-1][0:1]) 209 | if err != nil { 210 | return timeList, err 211 | } 212 | tempTime.startWeek = temp 213 | tempTime.week[temp] = 1 214 | tempTime.week[temp+5] = 1 215 | tempTime.week[0] = 2 216 | } 217 | //下面进行单双周判定 218 | splitFunc = func(r rune) bool { 219 | return r == '一' || r == '二' || r == '三' || r == '四' || r == '五' || r == '单' || r == '双' || r == '-' 220 | } 221 | // 1st Slice [一1-2单] 222 | for _, timePiece := range timeInfoSlice { 223 | 224 | // if timePiece == "上机" { 225 | // continue 226 | // } 227 | if tempTime.week[0] == 3 || tempTime.week[0] == 4 { 228 | tempTime.week[0] = 0 229 | } 230 | if strings.Contains(timePiece, "单") { 231 | // fmt.Println("Checked") 232 | for i := 1; i <= 10; i += 2 { 233 | tempTime.week[i] = 1 234 | tempTime.week[i+1] = 0 235 | } 236 | tempTime.week[0] = 3 237 | tempTime.startWeek = 1 238 | // fmt.Println(tempTime) 239 | } 240 | // fmt.Println(tempTime) 241 | if strings.Contains(timePiece, "双") { 242 | for i := 1; i <= 10; i += 2 { 243 | tempTime.week[i] = 0 244 | tempTime.week[i+1] = 1 245 | } 246 | tempTime.week[0] = 4 247 | tempTime.startWeek = 2 248 | } 249 | if tempTime.week[0] == 0 { 250 | for i := 1; i <= 10; i++ { 251 | tempTime.week[i] = 1 252 | } 253 | tempTime.week[0] = 5 254 | tempTime.startWeek = 1 255 | } 256 | switch timePiece[0:3] { 257 | case "一": 258 | tempTime.day = 1 259 | case "二": 260 | tempTime.day = 2 261 | case "三": 262 | tempTime.day = 3 263 | case "四": 264 | tempTime.day = 4 265 | case "五": 266 | tempTime.day = 5 267 | default: 268 | continue // ╰(*°▽°*)╯防止“上机”“学院机房上机”等情况 269 | } 270 | // fmt.Println(tempTime) 271 | // fmt.Println(timePiece) 272 | // 2nd Slice [1 2] 273 | timePieceSlice := strings.FieldsFunc(timePiece, splitFunc) 274 | startTime, err := strconv.Atoi(timePieceSlice[0]) 275 | if err != nil { 276 | return timeList, err 277 | } 278 | endTime, err := strconv.Atoi(timePieceSlice[1]) 279 | if err != nil { 280 | return timeList, err 281 | } 282 | tempTime.start[0], tempTime.start[1] = setTime(startTime, 1) 283 | tempTime.end[0], tempTime.end[1] = setTime(endTime, 2) 284 | timeList = append(timeList, tempTime) 285 | // fmt.Println(tempTime) 286 | } 287 | return timeList, err 288 | } 289 | 290 | func setTime(timeIdx, timeType int) (int, int) { 291 | // timeIdx := 1~12 292 | // timeType := 1,2 1 for StartTime, 2 for EndTime 293 | if timeType == 1 { 294 | switch timeIdx { 295 | case 1: 296 | return 8, 00 297 | case 2: 298 | return 8, 55 299 | case 3: 300 | return 10, 00 301 | case 4: 302 | return 10, 55 303 | case 5: 304 | return 13, 00 305 | case 6: 306 | return 13, 55 307 | case 7: 308 | return 15, 00 309 | case 8: 310 | return 15, 55 311 | case 9: 312 | return 18, 00 313 | case 10: 314 | return 18, 55 315 | case 11: 316 | return 20, 00 317 | case 12: 318 | return 20, 55 319 | } 320 | } else { 321 | switch timeIdx { 322 | case 1: 323 | return 8, 45 324 | case 2: 325 | return 9, 40 326 | case 3: 327 | return 10, 45 328 | case 4: 329 | return 11, 40 330 | case 5: 331 | return 13, 45 332 | case 6: 333 | return 14, 40 334 | case 7: 335 | return 15, 45 336 | case 8: 337 | return 16, 40 338 | case 9: 339 | return 18, 45 340 | case 10: 341 | return 19, 40 342 | case 11: 343 | return 20, 45 344 | case 12: 345 | return 21, 40 346 | } 347 | } 348 | return 0, 0 349 | } 350 | 351 | func WriteFile(filename string, data []byte, perm os.FileMode) error { 352 | f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) 353 | if err != nil { 354 | return err 355 | } 356 | n, err := f.Write(data) 357 | if err == nil && n < len(data) { 358 | err = io.ErrShortWrite 359 | } 360 | if err1 := f.Close(); err == nil { 361 | err = err1 362 | } 363 | return err 364 | } 365 | --------------------------------------------------------------------------------